スコープチェーン概要
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のクロージャの考え方につながってくるので重要です。