作用域链的形成过程

最开始说到过作用域 是函数创建时就决定了。当前作用域未查找到的变量对象会前往上一级作用域查找,直到作用域链的顶端。

函数创建时

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循环之前的步骤直到函数执行结束依次出栈