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オブジェクト |