技術はあとからついてくる。

技術はあとからついてくる。

就活開始の半年前にエンジニアに目覚めた人

JavaScriptのクロージャを理解する

クロージャとは

勉強I/O

  • 関数の中に定義している関数
  • ローカル変数を参照している

closure.js

function closure(initVal) {
    val count = initVal;
    
    var innerFunc = function() {
        return ++count;
    ]
    return innerFunc;
}

var myClosure = new closure(100);
console.log(myClosure); // 101
console.log(myClosure); // 102
console.log(myClosure); // 103

myClosure()を呼び出すたびに結果がインクリメントされていることが分かる。

通常、関数の中で定義されているローカル変数は、関数の処理が終わるとその時点で破棄される。
これは関数の実行が終われば、もうどこからも呼び出されることがないため。

しかしクロージャの場合は、ローカル変数(count)を参照している関数(inneFunc)が返却されるため innerFunc関数そのものはmyClosureに格納される。

つまり,myClosureがローカル変数(count)を参照し続けていることになる。
そのため、ローカル変数countは、myClosureが有効な限り破棄されない。

この概念をスコープチェーンを元に考えると closure.jsでは以下のスコープチェーンが生成される。

-先端-
|innerFunc
|Callオブジェクト

|closure
|Callオブジェクト

|Global
|オブジェクトp
-末端-

このスコープチェーンは、innerFunc関数が有効である間は保持され続ける。 innerFunc関数はmyClosureに格納されるため、closure関数が終了しても破棄されない。 (closure関数のCallオブジェクトも同様)

この結果、closure関数の終了後は

  • スコープチェーンが破棄されない
  • innerFunc関数のCallオブジェクトも破棄されない
  • Callオブジェクトに管理されているローカル変数も破棄されない(インスタンス化で代入された値は残ったままになる)

その結果、myClosure()が呼び出されるたびにローカル変数がインクリメントされることになる。

クロージャの使いどころ

クロージャは独立して動作させることができる

multi-closure.js

function closure(initVal) {
    var count = initVal;
    var innerFunc = function() {
        return ++count;
    };
    return innerFunc;
}

var myClosure = closure(10);
var myClosure2 = closure(20);

console.log(myClosure()); // 101
console.log(myClosure()); // 102
console.log(myClosure2()); // 201
console.log(myClosure2()); // 202

この時スコープチェーンは共通のGlobalオブジェクトを所有している。

|myClosure|      |myClosure2|
     |                |
  |   Globalオブジェクト    |