意識しないうちに利用しているクロージャだが人に教えるのはなんか難しい。
人に教えねばならぬ機会に恵まれたため、ここに復習の意味も込めて纏めておこうと思う。
関数を理解することが大事
クロージャを理解するにはまず関数の特性を理解するとこからだ。
というか関数を理解できたらそれはクロージャを理解できたことになると思う。
① スコープは関数定義時に決まる
関数内のスコープはローカルスコープとなりますがそれは関数定義時に決まります。
実行時ではありません。これを静的スコープといいます。
これはJavaScriptの関数が定義時のコンテキストで実行されることも意味します。
var scope = 'global';
function func1() {
console.log(scope);
}
function func2() {
var scope = 'local';
func1();
}
func1(); //global
func2(); //global
func1
はグローバルスコープに定義されているのでスコープチェーンによってscope
の値は「global」となる。
func2
はローカルスコープ内でfunc1
が呼び出されているが、実行コンテキストは定義時のコンテキストになるのでやはり結果は「global」となる。
② 関数は第1級オブジェクトである
第1級オブジェクトとは、通常のオブジェクトの様に、プロパティに定義したり、他の関数の引数として渡したり、戻り値にすることができるという意味です。これはJavaScriptの関数は定義されたコンテキストとは異なるコンテキスト上で実行できるということです。
var test = function () {
console.log('Hello World!!!');
},
test2 = function (fn) {
fn();
};
test2(test); //Hello World!!!
test
はグローバルに定義されていますが、実行はtest2
のローカルスコープ内です。
③ 定義時のコンテキストと異なるコンテキストから呼ばれる
この場合にクロージャとなる。①と②を踏まえて以下のコードを見てみる。
function makeCounter() {
var count = 0;
return f;
function f() {
return count++;
}
}
var a = makeCounter();
console.log(a()); // 0
console.log(a()); // 1
console.log(a()); // 2
まず関数makeCounter
はf
という関数を戻り値として返しています。
これは②の関数は第1級オブジェクトであるという特性に当てはまりますね。
このf
という関数はmakeCounter
の関数内で定義されているので関数内のスコープを参照することができます。これは①のスコープは関数定義時に決まるでした。(スコープチェーン)
そして、makeCounter
関数は変数a
に格納されグローバルスコープで実行されていますが、
実行コンテキストは関数宣言時のコンテキストとなるので結果count
という変数を参照し続けます。
纏め
- スコープは関数定義時に決まる
- 関数は第1級オブジェクトである
- 定義時のコンテキストと異なるコンテキストから呼ばれる
この3つがクロージャの本質となるので覚えておきましょう。