クロージャとスコープチェーン
クロージャをなるべく簡潔にまとめてみます。
まず、クロージャを正しく理解するためには以下の理解が不可欠なので、その説明をします。
- javascriptのスコープ
- スコープチェーン
- スコープは関数実行時ではなく、関数定義時に決定
javascriptのスコープ
そもそもスコープとは、ある変数や関数が特定の名前で参照される範囲のことです。
で、javascriptのスコープはというと、以下の3種類に分けられます。
- グローバルスコープ
- 関数スコープ(=ローカルスコープ)
- evalスコープ
3.は置いといて、1,2についてサンプルを交えて説明
var foo = 0; // グローバルスコープで定義 console.log(foo); // 出力:0 var myFunction1 = function(){ var foo1 = 1; // 関数スコープ(myFunction1)で定義 console.log(foo1); // 出力:1 // 入れ子な関数 var myFunction2 = function() { var foo2 = 2; // 関数スコープ(myFunction2)で定義 console.log(foo2); // 出力:2 }(); //console.log(foo2); // エラー }(); //console.log(foo1); // エラー
1行目のfooはグローバルスコープに定義された変数で、ソースのどこからでも参照可能です。
foo1,foo2はそれぞれの関数スコープ内で定義されており、その関数内であれば参照が可能ですが、関数外(12,14行目)では参照できません。
スコープチェーン
では、上記のmyFunction1内の入れ子な関数myFunction2内から、foo1は参照できるのでしょうか?
var foo = 0; // グローバルスコープで定義 console.log(foo); // 出力:0 var myFunction1 = function(){ var foo1 = 1; // 関数スコープ(myFunction1)で定義 console.log(foo1); // 出力:1 // 入れ子な関数 var myFunction2 = function() { var foo2 = 2; // 関数スコープ(myFunction2)で定義 console.log(foo2); // 出力:2 console.log(foo1); // 追加 出力:1 console.log(foo); // 追加 出力:0 }(); //console.log(foo2); // エラー }(); //console.log(foo1); // エラー
11,12行目でfoo1,fooにアクセスしてみると参照できました。
javascriptでは現在の関数スコープ内に指定された変数がない場合、その親関数の関数スコープ内を探しにいきます。
そして親関数にも変数がない場合には、さらにその親関数を探しにいき、最終的にはグローバルスコープに定義された変数まで確認します。
これをスコープチェーンと呼びます。
グローバルスコープに定義された変数がソースのどこからでも参照可能なのは、スコープチェーンの仕組みがあるからです。
スコープは関数実行時ではなく、関数定義時に決定
上記の関数スコープは実行時に変わることは一切ありません。
例えば、myFunction2が他の関数から参照され、そこで呼び出されたとしても、myFunction2内の変数の解釈はmyFunction2関数スコープ→myFunction1関数スコープ→グローバルスコープというルートを辿って検索されます。
つまり、myFunction2の中からは常にmyFunction1の変数が参照できるということです。
これは、たとえ無名関数であっても同様です。
改めてクロージャとは
改めて、クロージャのサンプルをみてみます。
var myFunction = (function(){ var foo = 'foo'; return function(){ return foo; } })(); console.log(myFunction()); // 出力:foo
3行目で、関数の戻り値として定義されている無名関数は、スコープチェーンにより、親関数内で定義されているfooにアクセスすることができます。
myFunctionにはこの関数が代入され、またスコープは実行時に変わることはないため、変数fooはこのmyFunctionを通じてしかアクセスすることができなくなってしまいました。
つまり、閉じ込められてしまいました。これがクロージャです。
。。。はいこれがクロージャです。。。と言われてもピンとこないですよね。。。
言い換えます。これがクロージャの仕組みです。
クロージャを使ってどんなことができるのかは別の機会に。。。