Javascriptの基本:スコープチェーン

スコープチェーン(scope chain)という言葉がある。
ここでいうスコープとは、「有効範囲」と言ったらいいかもしれない。よく「変数のスコープ」とか、「xxxのスコープ」とかいう言い方をするが、これらは、それぞれ「変数の有効範囲」、「xxxの有効範囲」という意味と考えればいい。

javascriptには可視性修飾子(javaでいうprivateとかpublicとか)がないので、「変数のスコープ」というと

  • グローバルスコープ
  • ローカルスコープ

の2種類だけが存在する。(これは、PHPなんかと同じ)

話がわき道にそれるようだが、javascriptは「オブジェクトの入れ子(マトリューシュカみたいな感じ)」になっている。
一番大きいオブジェクトがwindowオブジェクト、scriptタグの直下に関数などのオブジェクトxxxを作成すれば、「windowオブジェクト内のxxxオブジェクト」、そのまた下位にオブジェクトyyyをつくれば、「windowオブジェクト内のxxxオブジェクト内のyyyオブジェクト」となる。
ちなみに、HTMLタグでくくられる文書(DOMオブジェクト)は、windowオブジェクト内のdocumentオブジェクト内に存在する。

varによって変数が宣言されると、その変数は、

それを含む最小のオブジェクト

に所属することになり、そのオブジェクト内からのみ参照が可能になる。
これが、ローカル(局所的)変数の意味となる。

グローバルスコープの変数は、名前の通り、

どのオブジェクトからでも参照できる変数

である。これと同時に、

windowオブジェクトに属するローカル変数

である。

なんで、こんな回りくどい言い方をするかというと、javascriptでは、

下位のオブジェクトから変数を参照するとき、そのオブジェクトに変数が定義されていなければ、上位のオブジェクトにさかのぼって探しに行く

という機構が存在して、グローバル変数も、この機構によって「全てのオブジェクトから参照できる変数」になっているためである。

この機構のことを「スコープチェーン」という。

前置きが長くなってしまったが、サンプルを見た方が分かりやすいかと思う。

// グローバル変数=windowオブジェクト
var prop1 = "global scope"; // --- (1)

// 関数オブジェクト
var test = function(){ // --- (2)
	var prop1 = "test_func scope"; // --- (3)

	// 関数オブジェクトの中の関数オブジェクト
	var test1 = function(){ // --- (4)
		var prop1 = "test1_func scope"; // --- (5)
		alert(prop1); // --- (6)
	}

	test1();
	return;
};

というスクリプトがあって、イベントによってtest1()が実行されるとする。
このサンプルでは、

windowオブジェクト ==> testオブジェクト ==> test1オブジェクト

という入れ子ができあがっていて、それぞれのオブジェクト内に、prop1という変数が定義されている。

test()がよばれると、test1((4)式)が実行されるが、test1内の(6)式はprop1という変数を参照している。

上のスクリプトをそのまま実行すると「test1_func scope」という文字列がalertボックスに表示される。ここで、(5)式をコメントアウトすると、(上位オブジェクトであるtestオブジェクト内を検索して)「test_func scope」が表示される。さらに、(3)式をコメントアウトすると、(上位オブジェクトであるwindowオブジェクト=グローバル変数を検索して)「global scope」と表示される。