この記事の内容は、js の実行コンテキストと変数オブジェクトの分析に関するものです。必要な方は参考にしていただければ幸いです。
実行コンテキスト
JavaScript コードの実行プロセスには、コンパイルと実行の 2 つの段階が含まれます。コンパイルでは、字句解析を通じて抽象構文ツリーを構築し、JavaScript コードがコンパイルされる段階でそれを機械が認識する命令にコンパイルします。スコープのルールが決定されている; コードの実行段階で、または関数が呼び出されると、実行環境とも呼ばれる実行コンテキスト (実行コンテキスト) が作成されます
ECMA-262 には次の定義があります
コントローラー ECMA スクリプトの実行可能コードを入力すると、コントローラーは実行環境に入ります。現在アクティブな複数の実行環境が論理的にスタック構造を形成します。論理スタックの最上位の実行環境は、現在実行中の実行環境と呼ばれます。コントローラーが、現在実行中の実行環境に依存する実行可能コードから、その実行環境に依存しない実行可能コードに移行するたびに、新しい実行環境が作成されます。新しく作成された実行環境はスタックにプッシュされ、現在実行中の実行環境になります。
これも抽象的な概念ですが、1 つの JavaScript コード内で複数の実行コンテキストが作成され、変数または関数が定義されます。仕様や関連ドキュメントを読んで、実行コンテキスト (略して EC) には主に 3 つのポイントが含まれていることがわかりました。これらは、疑似コードで次のように表現されています。
EC = { this: // 绑定this指向为当前执行上下文, 如果函数属于全局函数,则this指向window scopeChain: [] // 创建当前执行环境的作用域链, VO: {} // 当前环境的变量对象(Variable Object),每个环境都有一个与之关联的变量对象 }
次のコード部分を見てください:
var a = 1; function foo() { var b = 2; function bar() { console.log(b) } bar() console.log(a); } foo()
1. これを実行します。このコードでは、最初にグローバル コンテキスト globalEC が作成され、実行コンテキスト スタックにプッシュされます。
3. bar() が呼び出されると、bar のコンテキスト barEC が作成され、実行コンテキスト スタックにプッシュされます
4. bar 関数が実行されると、 barEC は実行コンテキスト スタックからポップアップします
5. foo 関数が実行されると、fooEC が実行コンテキスト スタックからポップアップします
6. ブラウザ ウィンドウが閉じられた後、グローバル コンテキスト globleEC
概要: スタックの一番下は常にグローバル コンテキストであり、スタックの一番上は現在実行中のコンテキストです。ブラウザが閉じている場合にのみ、グローバル コンテキストがポップアップされます。実行コンテキスト スタック
各実行環境には、抽象概念である関連する変数オブジェクトがあり、環境内で定義されたすべての変数と関数がこのオブジェクトに格納されます。私たちが作成するコードはこのオブジェクトにアクセスできませんが、単一のパーサーはデータを処理するときにバックグラウンドでオブジェクトを使用します。 ブラウザが初めて js スクリプト プログラムをロードすると、デフォルトでグローバル実行環境に入り、グローバル環境変数オブジェクトは window になり、コード内でアクセスできます。
環境が関数の場合、このアクティブ オブジェクトを現在のコンテキストの変数オブジェクトとして使用します (VO = AO)。このとき、コードから変数オブジェクトにアクセスすることはできません。以下では主にアクティブ オブジェクトについて説明します。アクティベーションオブジェクト
1. アクティビティオブジェクト(以下AOと略します)を初期化します関数が呼び出されると、現在のコンテキストのアクティブオブジェクトが即座に作成され、アクティブオブジェクトは変数オブジェクトとして使用されます。引数属性 初期化、値は引数オブジェクトです (渡される実際のパラメータのセットは仮パラメータとは何の関係もありません。仮パラメータはローカル環境のローカル変数として定義されます)
AO = { arguments: <ArgO> };
arguments オブジェクトには次の属性があります:
length:callee:
'クラスインデックス':
function show (a, b, c) {
// 通过Object.prototype.toString.call()精准判断类型, 证明arguments不同于数组类型
var arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr)); // [object Array]
console.log(Object.prototype.toString.call(arguments)); // [object Arguments]
console.log(arguments.length) // 2 传递进来实参的个数
console.log(arguments.callee === show) // true 就是被调用的函数show自身
//参数共享
console.log(a === arguments[0]) // true
a = 15;
console.log(arguments[0]) // 15
arguments[0] = 25;
console.log(a) // 25;
但是,对于没有传进来的参数c, 和arguments的第三个索引是不共享的
c = 25;
console.log(arguments[2]) // undefined
argument[2] = 35;
console.log(c) // 25
}
show(10, 20);
実行環境に入る
実行するコード関数
2. 実行環境に入る
すべての仮パラメータ宣言:
実パラメータが渡された場合、その値はアクティブなオブジェクト属性として作成されます。パラメータが渡されない場合、値は未定義です
すべての関数宣言:変数の場合、値はアクティブなオブジェクトのプロパティとして作成されます。オブジェクトには同じ名前のプロパティがすでに存在するため、完全に置き換えられます。
すべての変数宣言:所有变量名称作为活动对象的属性被创建, 值为undefined,但是和函数声明不同的是, 如果变量名称跟已经存在的属性(形式参数和函数)相同、则不会覆盖
function foo(a, b) { var c = 10; function d() { console.log('d'); } var e = function () { console.log('e'); }; (function f() {}) if (true) { var g = 20; } else { var h = 30; } } foo(10);
此时在进入foo函数执行上下文时,foo的活动对象fooAO为:
fooAO = { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: fundefined, d: <d reference> //指向d函数的指针, e: undefined, g: undefined, h: undefined // 虽然else中的代码永远不会执行,但是h仍然是活动对象中的属性 }
这个例子做如下几点说明:
1.关于函数,只会创建函数声明作为活动对象的属性, 而f函数作为函数表达式并不会出现在活动对象(AO)中
2.e虽然值是一个函数, 但是作为变量属性被活动对象创建
3、代码执行阶段
在进入执行上下文阶段,活动对象拥有了属性,但是很多属性值为undefined, 到代码执行阶段就开始为这些属性赋值了
还是上面的代码例子, 此时活动对象如下:
fooAO = { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: 10, // 赋值为undefined d: <d reference> //指向d函数的指针, e: <d reference> // 指向e函数的指针 g: 20, h: undefined // 声明h变量,但是没有赋值 }
变量对象包括:{ arguments对象+函数形参+内部变量+函数声明(但不包含表达式) }
这时这个活动对象, 即作为当前执行环境的变量对象会被推到此执行环境作用域链的最前端(作用域链本篇不做介绍,会在下一篇文章中单独讲解作用域和作用域链), 假定执行环境为一个对象,则整个执行环境可以访问到的属性如下:
伪代码如下:
fooExecutionContext = { scopeChain: [], //fooAO +所有父执行环境的活动对象, fooAO: { arguments: { 0: 10, length: 1 }, a: 10, b: undefined, c: 10, // 赋值为undefined d: <d reference> //指向d函数的指针, e: <d reference> // 指向e函数的指针 g: 20, h: undefined }, this: 当前执行环境的上下文指针 }
补充:
下面的例子为了说明一下变量声明的顺序及变量同名不会影响函数声明
console.log(foo); // foo的函数体 var foo = 10; console.log(foo) // 10 function foo() {}; foo = 20; console.log(foo); // 20
在代码执行之前, 就会读取函数声明,变量声明的顺序在函数声明和形参声明之后, 整个流程如下:
进入执行环境阶段:
1. var VO = {} 2. VO[foo] = 'foo函数指针' 3. 扫描到var foo = 10, // 但是foo做为function已经声明,所以变量声明不会影响同名的函数声明,如果代码中没有foo函数声明的话,则foo为undefined
代码执行阶段:
1. VO[foo] = 10; 2. VO[foo] = 20;
解析代码完成。
相关推荐:
以上がjsの実行コンテキストと変数オブジェクトの分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。