函数执行上下文

71 阅读2分钟

函数执行上下文

函数调用时,才会创建上下文,分为两个阶段:

  1. 创建阶段
  • 创建VO(variable object)

    在 VO 中,变量和函数声明都会被存储为属性名,它们的值分别为 undefined 或函数对象。在函数执行时,JavaScript 引擎会按照以下顺序对 VO 进行处理:

    1. 创建 VO。
    2. 处理函数声明。将函数声明添加到 VO 中,并将函数对象赋值给相应的属性。
    3. 处理变量声明。将变量声明添加到 VO 中,并将 undefined 赋值给相应的属性。
    4. 处理形参。将函数参数添加到 VO 中,并将传入的实参赋值给相应的属性。
    5. 执行函数体内的代码。
  • 链接作用域链(scope)

  • 确定this指向

  1. 执行阶段
  • 激活VO为AO(activation object),vo和ao其实是一个变量对象在函数生命周期中的不同名字

VO的具体结构,包括形参、变量声明和Function声明:

function d(b=1){
    var a=0;
    function c(){}
}
=====> 该匿名函数的VO结构如下
{    
    b:undefined
    a:undefined
    c:<referrence fo function>
}

上述上下文栈状态如下

c:
{
    VO:{},
    scope: [Global_AO,d_AO],
    this: ? | globalThis
}

d:
{
    d_AO: {
            arguments:{
                0:1
                length:1
            },
            b:1,
            a:0,
            c: <referrence fo function>
        },
    scope:[Global_AO],
    this: ? | globalThis
}

global:
{
    Global_AO: {
            ...windows | ...global, //不是真的解构,只是为了表示
            d: <referrence fo function>
        }
    this: globalThis
}

这就可以解释为什么会有变量提升了:

  • 函数在进入创建阶段,就会创建变量对象,其中保存了所声明的变量,只是赋值并没有提升,所以变量都是undefined而函数则是响应的引用
  • 其中有一点,当函数名与变量同名时,VO会忽略变量声明防止覆盖函数,如下:
function(){
    console.log(c) //function c
    var c;
    function c(){}
    console.log(c) //function c
    c=1;
    console.log(c) //1
}

不过,这个是创建阶段提升的结果,执行阶段c被赋值还是会被修改的

  • 而如果是形参和变量重名的话,形参会覆盖变量,但是有时会报错,如下:
function(a){
    let a;
}

这个等同于

let a;
var a; // SyntaxError: Identifier 'a' has already been declared