作用域和作用域链是两个概念。
作用域:表示当前执行代码对变量的访问权限,具体参考系列的第二部分。
作用域链:是由当前执行上下文与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。
作用域是给的一个定义规则,而作用域链是JavaScript语言实际执行的情况。作用域链是在声明阶段确定的。
下面我们举个例子,同时回顾前面的几篇的内容
var a = 20;
function test() {
var b = a + 10;
function innerTest() {
var c = 10;
return b + c;
}
return innerTest();
}
test();
- 上面代码执行时,首先初始化全局对象VO和作用域链scopeChain,此时scopeChain只有全局的VO, 然后向ECS中push一个全局上下文globalcontent,
// 全局上下文
globalcontent = {
VO:{
a: 20
},
scopeChain:[VO(global)]
}
ESC = [globalcontent]
- 执行代码,当执行到test函数时,创建执行上下文testEC,包括初始化变量对象VO,同时向作用域链scopeChain中添加当前的VO,并将其push到栈中。
// test执行上下文
testEC = {
VO = {
arguments: {
0: undefined,
length: 0
}
b: undefined,
innerTest: <innerTest reference>, // 表示foo的地址引用
},
scopeChain:[VO(test),VO(global)]
}
ESC = [globalcontent, testEC]
- 当执行test函数时,testEC中的VO转变为AO,并重新赋值变量,如下
// test执行上下文
testEC = {
AO = {
arguments: {
0: undefined,
length: 0
}
b: 30,
innerTest: <innerTest reference>, // 表示foo的地址引用
},
scopeChain:[AO(test),VO(global)]
}
ESC = [globalcontent, testEC]
- 当执行到return语句时,创建执行上下文innerTestEC,包括初始化变量对象VO,同时向作用域链scopeChain中添加当前的VO,并将其push到栈中。
// innerTest执行上下文
innerTestEC = {
VO = {
arguments: {
0: undefined,
length: 0
}
c: undefined,
},
scopeChain:[VO(innerTest),AO(test),VO(global)]
}
ESC = [globalcontent, testEC,innerTestEC]
5、执行innerTest函数时,innerTestEC的VO转变为AO,并重新赋值变量,如下
// innerTest执行上下文
innerTestEC = {
AO = {
arguments: {
0: undefined,
length: 0
}
c: 10,
},
scopeChain:[AO(innerTest),AO(test),VO(global)]
}
ESC = [globalcontent, testEC,innerTestEC]
总结:
作用域链是由一系列变量对象组成的链条,其中链条的最前
端是当前执行环境的AO,最末端为全局对象。如果要查询变量对象的标识符,就可以通过这个链条,从前往后,逐层访问作用域中的变量。