先来复习一下,执行上下文的构成
执行上下文
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this的指向
之前说了变量对象,现在来说下另外两个
作用域链(Scope chain)
通俗地讲,当声明一个函数时,局部作用域一级一级向上包起来,就是作用域链。
我们从函数定义到执行一步一步来说明
当函数定义的时候
当函数被定义的时候,会读取函数内部的一个属性,[[scope]]属性来保存就会保存所有父变量对象到其中,可以理解 [[scope]] 就是所有父变量对象的层级链
function a(){
function b(){
}
}
分析一下,当函数创建时,各自的[[scope]]为
a.[[scope] = [
globalContext.VO
]
b.[[scope]] = [
aContext.AO,
globalContext.VO
];
当函数激活的时候
当函数激活的时候,创建VO/AO。并将其加入作用域链的第一位,作用域链(scope)到现在就创建完毕了
写个例子结合函数的上下文,变量对象等知识说明下作用域链
var global = '全局变量'
function abs(){
a = 1
console.log(a)
}
abs()
1.函数abs被创建,将作用域链保存到[[scope]]中
abs.[[scope]] = [
globalContext.VO
]
2.abs函数开始执行,此时创建abs函数的执行上下文,并入栈
ECstack = [
absContext
globalContext
]
3.abs做正式执行前的准备工作,将[[scope]]属性作为作用域链,此时的abs执行上下文
absContext = {
Scope: abs.[[scope]],
}
4.初始化变量对象,VO/AO
absContext = {
AO: {
arguments: {
length: 0
},
a: undefined
},
Scope:abs.[[scope]]
}
5.将活动对象AO加到作用域链的顶端
absContext = {
AO: {
arguments: {
length: 0
},
a: undefined
},
Scope:[AO,abs.[[scope]]]
}
至此,准备工作完成
6.随着函数的进行,AO开始进行赋值工作
checkscopeContext = {
AO: {
arguments: {
length: 0
},
a: 1
},
Scope: [AO, [[Scope]]]
}
7.在作用域链中找到a的值并打印
8.abs执行完毕,abs的执行上下文栈弹出
ECstack = [
globalContext
]