JavaScriptデバイスについて多くの記事が書かれています。 まず、 「JavaScript。The kernel」です。 ドミトリーソシュニコフ 、 リチャードコーンフォードによる記事の翻訳、およびドミトリーフランクによる投稿 。 しかし、技術を十分に把握するには、主要な情報源に目を向けることをお勧めします。 この場合、 ECMA-262標準ECMAScript言語仕様に準拠します。 この投稿は、標準の調査を開始するための簡単な方法だと思います。 リンクをたどり、仕様書を読み、独自の図を作成することをお勧めします。
JavaScriptクロージャーの仕組み
主なECMAScript構造は、 実行コンテキスト 、 語彙環境 、および環境レコードです。 それらは次のように接続されます。
VariableEnvironmentに加えて、 実行コンテキストにはLexicalEnvironmentもあります。違いの詳細については、 ECMAScript 5の仕様(LexicalEnvironmentとVariableEnvironment)を参照してください 。
ThisBindingについては、記事の次のパートで説明します。
環境レコードには、変数の値が格納されます。
var a = 1
を宣言すると、現在のレコードに {a:1}が表示されます。 字句環境では 、 レコードのほかに、 外部フィールドもあります。 外部は、たとえば、ネストされた関数の場合の外部レキシカル環境を指します。
変数の検索は、現在のコンテキストのVariableEnvironmentで始まります。 この名前の変数がレコード内に見つからない場合、 外部環境でチェーンによって検索されます 。
プログラムが起動すると、グローバルコンテキストと環境が作成されます。 レコードとして、 グローバルオブジェクトが使用されます。
インタープリターがfunctionキーワードを検出すると、 FunctionObjectを作成します。 現在のレキシカル環境への参照は、作成されたFunctionObjectの scopeプロパティに書き込まれます。
表記
function f(){}
、表記
var f = function(){}
function f(){}
ほぼ同等です。ただし、最初の場合は、
function f()
を含むブロックに入るとFunctionObjectが作成され、2番目は特定の行を実行するときに作成されます。
関数が呼び出されるたびに、新しいコンテキスト 、 環境 、およびレコードが作成されます。 コンテキストはスタックにプッシュされ、関数を終了すると破棄されます。 作成された環境の 外側に 、呼び出されたFunctionObjectの スコープ が書き込まれます。 関数がグローバルコンテキストで宣言された場合、 outerはグローバル環境を指します 。
ここで、ある関数が別の関数を返すときの閉包を検討します。
var x = 1; function f() { var x = 2; function g() { return x; } return g; } f()();
関数fが呼び出されると、
function g
FunctionObjectと同様に、 fの コンテキストと環境 が作成されます。 スコープは、 VariableEnvironmentから現在の環境への参照を書き込みます。 fを終了すると、コンテキストは破棄されますが、返されるFunctionObjectからのリンクがあるため、 環境は残ります。
fから返された関数g が呼び出されると、 gのコンテキストと環境 が作成されます。 新しい環境の外側は、呼び出されたFunctionObjectからスコープを書き込みます。 変数xの検索は、現在のVariableEnvironmentで始まり、次にouterまで続きます。 その結果、値
x = 2
が返されます。
記事の次の部分では 、ECMAScriptの観点からこれがどのように機能するかを調べます。