スコープチェーン概要
javascriptを理解する上で重要な考え方としてスコープチェーンという考え方があります。
javascriptでの変数のスコープは、グローバル変数ではプログラム全体で有効になり、
ローカル変数は宣言された関数と、その関数内でネストされた関数内で有効になります。
実際の例として、以下のようなコードを書いて実行してみます。
//グローバル変数 var test = "1"; console.log("グローバル変数 : " + test); function scope() { //ローカル変数 var test = "2"; console.log("ローカル変数1 : " + test); function nest() { //ネストされた関数内のローカル変数(再定義は無し) console.log("ネスト内のローカル変数 : " + test); //関数内のローカル変数の値を返す return test; } //このタイミングで、関数から返された「3」を返す return nest(); } console.log("関数呼び出し : " + scope()); console.log("グローバル変数 : " + test);
実行した結果は次のようになります。
グローバル変数 : 1 ローカル変数1 : 2 ネスト内のローカル変数 : 2 関数呼び出し : 2 グローバル変数 : 1
先ほどの「ローカル変数は宣言された関数と、その関数内でネストされた関数内で有効」ということが上記の出力結果の「ネスト内のローカル変数 : 2」という出力結果になることで、理解できます。
上記の例のように、関数内で変数を宣言する場合や、グローバル変数を宣言する場合、
そのコード(ここでは変数の宣言)に関連づけられたスコープチェーンが存在します。
スコープチェーンはそのコードのに対してスコープ内で変数を定義するオブジェクトのリストと言えます。
ローカル変数のスコープチェーン
ローカル変数aを宣言する際、次のような処理がされます。
・変数aのチェーンの先頭を調べる
・aというプロパティを持つ場合、その値が定義される
・もし、プロパティを持たなかった場合、チェーンの次のオブジェクトを探す
・次のオブジェクトでaというプロパティがあれば、その値が定義される
・もし、なければ、同様にチェーンの次のオブジェクトを探す
・最終的にプロパティが見つからなければ、ReferenceErrorとなる
トップレベルのグローバル変数のスコープチェーン
ローカル変数ではなく、トップレベルのグローバル変数の場合は次のようになります。
・グローバル変数は一つだけ定義される
・関数1つに2つのスコープチェーンが存在する(1つ目のスコープチェーンは関数の引数、ローカル変数、2つ目はグローバルオブジェクト)
・ネストされた関数の場合、3つ以上のスコープチェーンになる
・ネストされた関数が定義された時、スコープチェーンが保持される
・ネストされた関数が呼び出された時、オブジェクトが生成される
・ローカル変数が保存される
・上記の生成されたオブジェクトをスコープチェーンに追加する
・関数呼び出しのスコープチェーンを表す新たなスコープチェーンを生成
簡易的に書きましたが、次のネストされた関数の場合と合わせて、全体像を理解することがポイントになります。
ネストされた関数の場合
ネストされた関数の場合は、次のような動きになります。
その側の関数が呼び出されるたびに内側の関数が再び定義され、
外側の関数の呼び出しごとに、スコープチェーンが異なって動作します。
このスコープチェーンが異なって動作する。という考え方がjavascriptのクロージャの考え方につながってくるので重要です。