JavaScript 実行コンテキスト
Rev.14を表示中。最新版はこちら。
O'REILLY JavaScript 第3版 のスコープあたりを読んでいて混乱してきたので、整理がてらメモ。オブジェクト
グローバルオブジェクトグローバル変数がオブジェクトのプロパティとして格納される。常に1つだけ存在する。
Callオブジェクト
関数の実行中、変数や引数がオブジェクトのプロパティとして格納されている。関数を実行する度に作成される。
実行コンテキスト
関数を実行するたびに新しいコンテキストを作成する。スタック構造をしており、スタックのTopが現在の実行コンテキスト。(スタックフレームのようなもの)
- 関数外部(グローバル)のJavascriptコード実行時の実行コンテキストはグローバルオブジェクト
- 関数内部のJavascriptコード実行時の実行コンテキストは関数実行時に作成されたCallオブジェクト
実行コンテキスト==グローバル/Callオブジェクトっぽい書きかたがしてある。
スコープチェーン
スコープチェーンはグローバル/Callオブジェクトのリスト。変数を参照する際に、スコープチェーンの末端から順番に探される。リストのトップはグローバルオブジェクトとなる。実行コンテキスト毎に1つのスコープチェーンが存在する。関数が実行されて新しいコンテキストが作られるとスコープチェーンも作られる。
静的スコープ
関数実行時に使用するスコープチェーンは関数定義時に決定される(関数が定義された時点のスコープチェーンが使用される)。最も基本的なケースであるグローバル領域で関数を定義した場合は、関数定義時はグローバルオブジェクト一つのスコープチェーンであるため、これを使用することになる。このチェーンに呼び出した関数のCallオブジェクトが追加される。
以下に例を示す。
グローバル領域で関数定義をした場合
x=1; function f() {var y=2;g();} function g() {var z=3;} f();
(1) f(),g()定義時のスコープチェーン
+------+ | x=1 | Global Obj. +------+f(),g()にはこのスコープチェーンが使用される。
(2) f()が呼び出されると...
+------+ | x=1 | Global Obj. +------+ <--+ | +------+ ---+ | y=2 | f() Call Obj. +------+(1)のスコープチェーンにf()を呼び出した時に作成されたCall Obj.がチェーンされる。y,xはスコープチェーンにあるので参照できるが、zは参照できない。
このあたりはCとかと同じなので問題なし。
(3)f()からg()が呼び出されると...
+------+ | x=1 | Global Obj. +------+ <--+ | +------+ ---+ | z=3 | g() Call Obj. +------+g()も(1)のスコープチェーンを使用するので、(1)にg()を呼び出した時に作成されたCall Obj.がチェーンされる。
g()から参照できるのはz,xのみ。これも問題なし。
関数領域に関数定義をした場合
x=1; function f() { var y = 2; g(); function g() {var z=3;} } f();
(4) g()定義時のスコープチェーン
+------+ | x=1 | Global Obj. +------+ <--+ | +------+ ---+ | y=2 | f() Call Obj. +------+f()が呼び出されて初めてg()が定義されている
(5) f()を呼び出して、f()からg()が実行されると...
+------+ | x=1 | Global Obj. +------+ <--+ | +------+ ---+ | y=2 | f() Call Obj. +------+ <--+ | +------+ ---+ | z=3 | g() Call Obj. +------+g()が定義された時のチェーンは(4)なので、(4)にg()呼び出し時のCall Obj.がチェーンされる。
g()からはx,y,zが参照可能。このあたりもまだ、直感的にOK。
おまけ:呼び出される度に関数を返す関数
function f(x) { return function () {return x} } func1 = f(1); func2 = f(2); alert(func1()); <== 1と表示 alert(func2()); <== 2と表示
関数{return x}はf(x)を実行するたびに作成(定義)される。このため、f(1),f(2)で{return x}が作成された時のそれぞれのスコープチェーンは以下のようになる。
(6) f(1)で{return xが定義されたときのスコープチェーン}
+------+ | | Global Obj. +------+ <--+ | +------+ ---+ | x=1 | f(1) Call Obj. +------+(7) f(2)で{return xが定義されたときのスコープチェーン}
+------+ | | Global Obj. +------+ <--+ | +------+ ---+ | x=2 | f(2) Call Obj. +------+このため、func1(),func2()を呼び出すとそれぞれ、以下のスコープチェーンになる。
(8) func1()呼び出し時のスコープチェーン
+------+ | | Global Obj. +------+ <--+ | +------+ ---+ | x=1 | f(1) Call Obj. +------+ <--+ | +------+ ---+ | | {return x} Call Obj. +------+(6)にfunc1()呼び出し時のCall Obj.がチェーンされる。
(9) func2()呼び出し時のスコープチェーン
+------+ | | Global Obj. +------+ <--+ | +------+ ---+ | x=2 | f(2) Call Obj. +------+ <--+ | +------+ ---+ | | {return x} Call Obj. +------+(6)にfunc2()呼び出し時のCall Obj.がチェーンされる。
よって、func1()では1が返り、func2()では2が返るようになる。