#JavaScriptの実行コンテキストとキュー(スタック)の関係?
6339 ワード
実行コンテキストとは?
JavaScriptでコードが実行される場合、その実行環境は非常に重要であり、以下のクラスに分けられます.
ここでは特に何もありませんが、1つのglobal contextは紫色の枠線で表され、3つの異なるfunction contextsはそれぞれ緑、青、オレンジ色の枠線で表されます.プログラム内の他のコンテキストからアクセスできるglobal contextは1つしかありません.
任意の数のfunction contextsを持つことができ、各関数呼び出しによって新しいコンテキストが作成され、現在の関数の役割ドメイン外から関数内部宣言の内容に直接アクセスできないプライベート役割ドメインが作成されます.上記の例では、関数は現在のコンテキストの外で宣言された変数にアクセスできますが、外部コンテキストは内部で宣言された変数/関数にアクセスできません.どうしてこんなことになったの?このコードはいったいどのように実行されていますか?
コンテキストスタックの実行
ブラウザのJavaScriptインタプリタの単一スレッドが実行されます.これは、ブラウザが同じ時間に1つのことしか実行しないことを意味し、他のイベントが実行キューに並んでいることを意味します.次の図は、単一スレッドキューの抽象ビューです.
ブラウザがスクリプトを最初にロードすると、デフォルトでグローバル実行コンテキスト(global execution content)に入ります.グローバルコードで関数を呼び出すと、プログラムのシーケンスストリームが呼び出された関数に入り、新しい関数execution contextを作成し、コンテキストを上部execution stack(実行キュー)にプッシュします.
現在の関数で別の関数を呼び出すと、同じことが起こります.コードの実行プロセスは内部関数に入り、execution contextを作成し、実行キューの上部にプッシュします.ブラウザは常にスタックの上部にあるexecution contextを実行し、関数が現在の操作execution contextの実行を完了すると、スタックの上部からポップアップされ、現在のスタックの下のコンテキストに制御権が戻ります.次の例では、再帰関数とプログラムexecution stackを示します.
(function foo(i) {
if (i === 3) {
return;
}
else {
foo(++i);
}
}(0));
コードは自身を3回だけ呼び出し、iの値を1ずつインクリメントする.foo関数を呼び出すたびに、新しい実行コンテキストが作成されます.実行が完了すると、スタックがポップアップされ、global context(koa 2のタマネギ図は思いついたか?)に再び到達するまで下のコンテキストに制御権が渡されます.
以下に、実行キューの5つのキーを示します.
したがって、関数を呼び出すたびに新しい実行コンテキスト(execution context)が作成されることがわかりました.ただし、JavaScriptインタプリタでは、生成実行コンテキスト(execution context)を呼び出すたびに2つのフェーズがあります.
executionContextObj = {
'scopeChain': { /* variableObject + all parent execution context's variableObject */ },
'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },
'this': {}
}
アクティブ/変数オブジェクト[AO/VO]
このexecutionContextObjは、関数を呼び出すときに作成されますが、実際の関数を実行する前に作成されます.これが最初のフェーズです.フェーズを作成します.ここで、インタプリタは、入力されたパラメータまたはarguments、ローカル関数宣言、およびローカル変数宣言をスキャンすることによってexecutionContextObjを作成する.今回のスキャンの結果はexecutionContextObjになりました.variableObject.
次は、解釈器がコードの偽の解析方法の概要です.
例を見てみましょう
function foo(i) {
var a = 'hello';
var b = function privateB() {
};
function c() {
}
}
foo(22);
呼び出し時foo(22)、creation stage長は次のようになります.
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: undefined,
b: undefined
},
this: { ... }
}
ご覧のように、creation stageは属性のnameを定義し、値を割り当てませんが、formal arguments/parameters(関数パラメータ、arguments)を除きます.creation stageが完了すると、実行プロセスは関数体に入り、関数が完了した後のexecution stageは次のようになります.
fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: { ... }
}
アップグレード
多くのJavaScriptの資料では、解釈変数と関数宣言がその役割ドメインの上部に昇格した.しかし、なぜこのようなことが起こったのかを詳しく説明する人はいません.解釈器がactivation objectをどのように作成するかを把握すると、理解しやすくなります.例:
(function() {
console.log(typeof foo); // function pointer
console.log(typeof bar); // undefined
var foo = 'hello',
bar = function() {
return 'world';
};
function foo() {
return 'hello';
}
}());
私たちが今答えられる質問は、
概要
JavaScriptインタプリタがコードをどのように実行するかを今までよく把握しておきたい.実行コンテキストとキューを理解すると、コードが予想されなかった理由がわかります.
インタプリタの内部動作原理を理解することはJavaScriptの知識の重要な構成部分だと思いますか?実行コンテキストの各フェーズがJavaScriptの作成に役立つかどうか知っていますか?
__注意_::閉パッケージ、コールバック、タイムアウトなどについて質問している人もいますが、次の記事では、役割ドメインチェーンとexecution contextの関係を主に概説します.
広がる