クロージャJavaScript中級者への道②

JavaScript

前回の記事では、Ajaxを使ってサーバーからデータを取ってくるということをやりました。

今回はクロージャという物を勉強しました。
まずはスコープというものを確認しておきます

    console.log("--------------------SATRT-----------");

    {
        const value = 100;
    }

    console.log(value);

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:13 Uncaught ReferenceError: value is not defined
    at index.html:13
(anonymous) @ index.html:13

スコープの外からスコープの中にあるvalueにはアクセスできませんよ。というかスコープの外では宣言されていませんよ。というエラーになっている。

スコープの内からスコープの外は参照できる。

    console.log("--------------------SATRT-----------");


    let global;

    {
        const value = 100;
        global = value;
    }

    console.log(global);

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:16 100
index.html:18 --------------------END-----------

では次はどうなるか?

    console.log("--------------------SATRT-----------");

    let global;

    {
        const value = 999;
        global = function(){
            return value;
        }
    }

    console.log(global());

    console.log("--------------------END-----------");

globalがスコープ内のvalueを返却する関数に生まれ変わり、そのglobalを呼び出しています。
先ほどの例ではスコープの外からvalueにアクセスすることはできませんでしたが、、、、

--------------------SATRT-----------
index.html:18 999
index.html:20 --------------------END-----------

今回の結果はスコープ内のvalueを返す関数をスコープ外から呼び出すと、スコープ内のvalueにアクセスできてしまいした。

普通はvalueはスコープから抜けると消え去るはずですが、なんとglobalを呼び出した時に、確実に存在しているのです。そのためコンソールには999が表示されています。

まさに、globalという関数がスコープを保持しているようですね。

ではスコープを保持しているというならば、そのスコープは一つであるのか?スコープがあるメモリを参照してるのか?(何言ってんだ俺は)

    console.log("--------------------SATRT-----------");


    function getValueFunc(x){
        let value = x;
        
        return function(y){
            return value + y;
        }
    }

    let funcA = getValueFunc(10);
    let funcB = getValueFunc(1);

    /*
    funcA = 
        return function(y){
            return value + y;
        }
    funcAはyを引数に受け取る関数。

    funcAの中身↓
    ƒ (y){
            return value + y;
        }
    */

    console.log(funcA(10));
    console.log(funcB(1));


    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:33 20
index.html:34 2
index.html:37 --------------------END-----------

これはMDNのクロージャの説明を真似ました。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures

let funcA = getValueFunc(10); // xが10
let funcB = getValueFunc(1); // xが1

console.log(funcA(10));呼び出した時にxは1じゃなくて10になっている

つまりクロージャが持っている関数の定義は同じだけど、持っている環境は違いますよというお話。

クロージャがスコープ環境を保持しているとは?僕は最初言っていることが理解できなかった。でもこの例で少しわかってくる

    console.log("--------------------SATRT-----------");

    function getValueFunc(x){
        let value = x;
        
        return function(y){
            value += y;
            return value;
        }
    }

    let func = getValueFunc(10);
    console.log(func(1));
    console.log(func(1));
    console.log(func(1));
    console.log(func(1));
    console.log(func(1));
    console.log(func(1));
    console.log(func(1));

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:20 11
index.html:21 12
index.html:22 13
index.html:23 14
index.html:24 15
index.html:25 16
index.html:26 17
index.html:36 --------------------END-----------

スコープから抜けると変数が消滅するはずだが、この例では呼び出す度に、valueの値が増えている。

funcというクロージャは、ただの関数でなくてスコープの値を保有しているようだ。

つまり。。。

クロージャクラスのようなものじゃまいか!?

class Closure{
let value;
function __constructor(x){
value=x;
}
function addvalue(y){
value+=y;
return value;
}

こんな感じ・・・イメージ的には。

おまけ巻き上げ

中級者なら知っておくべき巻き上げ(ホイスティング)こいつもついでに勉強したので、ついでにメモっときます。

    console.log("--------------------SATRT-----------");
    
    console.log(name);
    let name = "edogawa konan";

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:8 Uncaught ReferenceError: Cannot access 'name' before initialization
    at index.html:8

初期化される前のnameにアクセスしてんじゃねえぞというエラーです。
ではletでなくvarで宣言するとどうでしょうかというお話ですよ

    console.log("--------------------SATRT-----------");

    console.log(name);
    var name = "edogawa konan";

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:8 
index.html:11 --------------------END-----------

エラーになってないんですね。これリロードすると結果が変わるですけど、、なんでですか?

リロードした結果
--------------------SATRT-----------
index.html:8 edogawa konan
index.html:11 --------------------END-----------

関数も巻き上げられます。

    console.log("--------------------SATRT-----------");

    func();
    function func(){
        console.log("edogawa konan");
    }

    console.log("--------------------END-----------");
--------------------SATRT-----------
index.html:10 edogawa konan
index.html:13 --------------------END-----------

宣言前のfuncを実行することができます。
ただし変数にぶち込まれた関数は巻き上げられません。

    func();
    const func = function(){
        console.log("edogawa konan");
    }
--------------------SATRT-----------
index.html:8 Uncaught ReferenceError: Cannot access 'func' before initialization
    at index.html:8
(anonymous) @ index.html:8

コメント

タイトルとURLをコピーしました