作用域链的形成过程
最开始说到过作用域 是函数创建时就决定了。当前作用域未查找到的变量对象会前往上一级作用域查找,直到作用域链的顶端。
函数创建时
function father() {
var a = 'a in father'
function son () {
var a = 'a in son'
}
}
father.[[scope]] = [
globalContext.VO //全局变量对象
]
函数会有一个内部属性[[scope]]保存着该函数作用域相关的信息。此时函数创建时生成的[[scope]]并不是完整的作用域链。
而father函数没有调用,不会生成father的执行上下文,所以也不会有son的作用域链生成(此时JS引擎并不认识son).
函数调用时
function father() {
var a = 'a in father'
function son () {
var a = 'a in son'
}
son()
}
father()
father调用时
//fatherContext进入ECStack
ECStack = {
fatherContext,
globalContext
}
//产生执行上下文
fatherContext = {
scope: father.[[scope]]
//复制father函数在创建时生成[[scope]]到作用域链属性中
}
//初始化AO 活动对象
fatherContext = {
AO:{
arguments: { //保存着函数的形参, 类数组对象
length:0
}
son: function
//此时js引擎才认识son是一个函数,为son创建[[scope]]内部属性
},
scope: father.[[scope]]
}
//将AO加到作用域链的前端
fatherContext.scope = [fatherContext.AO,father.[[scope]]];
//开始执行函数,发现son被调用,同father一样,
//js引擎将控制权交给sonContext循环之前的步骤直到函数执行结束依次出栈