前面的文章提到:当控制进入一个执行上下文时,会创建并初始化一个作用域链。这篇文章来了解下作用域链及其作用。。。

一. 什么是作用域链?

它是一个对象列表,在控制进入一个执行上下文时被创建。

二. 它的作用是什么?

在ECMAScript 262-3中,有两处提到了它的作用:

1. 8.6.2一节,关于函数[[Scope]]属性的描述
”A scope chain that defines the environment in which a Function object is executed.“

2. 10.1.4一节,对作用域链的介绍
“A scope chain is a list of objects that are searched when evaluating an Identifier.”

三. 什么是标识符解析?

我的理解:它是对标识符(即指类,方法,变量的名字)进行求值的过程。

四. 标识符的解析逻辑

在ECMAScript 262-3中10.1.4一节,对标识符的解析逻辑描述如下:

// 假设带解析的标识符为P
1. 获取当前作用域链上的下一个对象O;
2. 检查对象O上是否存在属性P;
3. 如果存在,则返回属性值;否则,再次获取作用域链上的下一个对象O,继续查找;
4. 如果在作用域链上没有查找到属性P,则返回undefined。
例_1:
var a = 1;
function test() {
    alert(a);
}
test(); // 1

说明:// 假设EC表示执行上下文,VO表示变量对象
1. 当调用test函数时,创建了一个新的执行上下文:EC_test,其对应的作用域链近似地表示为:[VO_global, VO_test];
2. 当执行"alert(a)"时,对标识符a进行解析:首先到VO_test上去查找,发现VO_test上不存在属性a,接着到VO_global上查找;
3. 由于VO_global上存在属性a,所以返回1.

五. with和catch对标识符解析的影响

在执行代码的过程中,with语句和catch子句均会加深当前执行上下文的作用域链,由于解析标识符时会查找作用域链,所以这会对其产生影响。

1. with语句的影响

当with语句被执行时,会添加一个变量对象到当前作用域链的顶部,这个变量对象包含了with所指定对象的所有属性,一旦with语句执行结束,这个变量对象就从作用域链顶部被移除。

例_1:
var a = 1;
with({a: 2}) {
    alert(a); // 2
}
alert(a); // 1

说明:// 假设VO表示变量对象
1. 当执行至with语句时,当前执行上下文的作用域链近似地表示为:[VO_global, VO_with];
2. 当执行至第一个"alert(a)"时,对标识符a进行解析,首先从VO_with上进行查找,由于其上存在该属性,所以输出2;
3. 当with语句执行结束时,VO_with从作用域链中移除,此时的作用域链近似地表示为:[VO_global];
4. 当执行至第二个"alert(a)"时,再次对标识符a进行解析,在VO_global上找到了属性a,所以输出1。

2. catch子句的影响

当try…catch语句块中的catch子句被执行时,会添加catch指定命名的异常对象到作用域链的顶部,一旦catch语句执行结束,这个异常对象从作用域链顶部被移除。

var a = 1;
try {
    throw 2;
} catch(a) {
    alert(a); // 2
}
alert(a); // 1

说明:// 假设VO表示变量对象
1. 当执行至catch子句时,当前执行上下文的作用域链近似地表示为:[VO_global, VO_catch];
2. 当执行至第一个"alert(a)"时,对标识符a进行解析,首先从VO_catch上进行查找,由于其上存在该属性,所以输出2;
3. 当catch子句执行结束时,VO_catch从作用域链中移除,此时的作用域链近似地表示为:[VO_global];
4. 当执行至第二个"alert(a)"时,再次对标识符a进行解析,在VO_global上找到了属性a,所以输出1。

六. 参考资料

1. ecmascript 262-3(pdf)

,

Leave a comment!