前端学习之函数执行上下文

184 阅读3分钟

1.0.0 函数的执行环境(执行上下文)

每个函数的执行,都会创建一个与该函数相关的函数执行环境,又称函数执行上下文(内部对象,可理解为作用域)。

1.1.0 产生执行上下文的两个阶段

1.1.0.1 创建阶段

当函数被调用,但未执行任何内部代码之前;

  • 创建作用域链(Scope Chain);
  • 生成变量对象(函数的形参、函数声明、变量声明);
  • this的指向;


1.1.0.2 代码执行阶段

  • 创建状态负责处理定义属性的名字,不为其指派具体的值,以及形参/实参的处理;
  • 一旦创建阶段完成,执行流进入函数并且激活/代码执行阶段,Execution Context object就会更新。


1.1.0.3 执行完毕(垃圾回收机制)

函数执行完毕,等待被回收


1.2.0 变量对象

基本概念:

  • 变量对象(varibale object)是与执行上下文相关的数据作用域
  • VO是与上下文关联的特殊对象,用于存储被定义在上下文的变量函数声明
  • VO是一个抽象的概念,不同的执行上下文中,它表示不同的Object。例如在global全局上下文中,变量对象也是全局对象自身[global object](即:我们可以通过全局对象的属性来指向全局变量);


变量对象的创建:

  • 建立arguments对象。检查当前上下文中的参数,建立该对象下的属性和属性值;
  • 检查当前上下文的函数声明,即通过function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在的内存地址的引用;如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖;
  • 检查当前上下文中的变量声明,每找到一个变量声明,就会在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改;

function test () {
    console.log(a);
    console.log(foo());

    var a = 1;
    function foo () {
        return 2;
    }
}
test();

上述test运行时的执行上下文创建过程如下:

testEC = {
    // 变量对象
    VO: {},
    scopeChain: {},
    this
}

VO = {
    arguments: {...}, // 注:再浏览器的展示中,函数的参数可能并不是放在arguments对象中
    foo: <foo reference>, // 表示foo的地址引用
    a: undefined
}

注意:代码未进入执行阶段之前,变量对象中的属性都不能访问;但是进入执行阶段之后,变量对象转换为活动对象,里面的属性都能被访问,然后进行执行阶段的操作;

变量对象活动对象其实是一个对象,只是处于执行上下文的不同生命周期;

// 执行阶段
VO -> AO
AO = {
    arguments: [...],
    foo: <foo reference>,
    a: 1
}

因此上面的代码会变成:

function test() {
    function foo() {
        return 2;
    }
    var a;
    console.log(a);
    console.log(foo());
    a = 1;
}
test();


1.2.1.1 全局作用域下的变量对象

以浏览器为例,全局对象为window;

全局作用域下有一个特殊的地方,它的变量对象,就是window对象;而这个特殊,在this指向上也同样适用,this也是指向window。

windowEC = {
    VO: window,
    scopeChain: {},
    this: window
}

除此之外,全局作用于下的生命周期与程序的生命周期一致;