# 前言

执行上下文有如下 3 个重要属性:

  • 变量对象
  • 作用域链
  • this

# 变量对象

变量对象时用于存储上下文中定义的变量和函数声明,全局上下文的变量对象就是全局对象(例如浏览器环境中的 window)

# 函数上下文中的变量对象

在函数上下文中我们用活动对象(activation object, AO) 来表示 变量对象

活动对象和变量对象其实是同一个对象,只有当进入一个执行上下文中,这个变量对象才会被激活, 被激活的对象叫活动对象(AO),它通过函数的 arguments 属性初始化

# 变量对象(VO)和活动对象(AO)的区别

在未进入执行上下文之前,VO 中的属性不能访问,进入执行上下文之后 VO 变成 AO,AO 中的变量便可以访问, 同一个对象,处于执行上下文的不同声明周期

# 进入执行上下文

进入执行上下文,这时候代码还没执行,变量对象会包括:

  • 函数的所有形参(即传入函数的参数)
    • 没有实参,值为 undefined
  • 函数声明
    • 值为函数对象
    • 如果变量对象存在相同属性名,则完全替换这个属性
  • 变量声明
    • 值为 undefined
    • 如果变量对象跟已声明的形参或函数同名,则变量声明不会干扰已存在的这类属性( 注 ① )
function foo(a) {
  var b = 2;
  function c() {}
  var d = function () {};
  b = 3;
  // ① 对 a b c 重新申明(不赋值),并不会对之前的变量有任何干扰
  var a, b, c;
}

foo(1);
1
2
3
4
5
6
7
8
9
10

在进入执行上下文之后,AO 是:

AO = {
  arguments: {
    0:1,
    length: 1
  },
  a:1,
  b: undefined,
  c: reference to function c() {},
  d: undefined
}
1
2
3
4
5
6
7
8
9
10

因为进入执行上下文时,代码还没有执行,所以其中的声明的变量值都是 undefined,但声明的函数 会对应一个函数的引用,注意 var c = function () {} 这种不叫函数声明

但执行完代码之后,AO:

AO = {
  arguments: {
    0:1,
    length: 1
  },
  b: 2,
  c: reference to function c() {},
  d: reference to FunctionExpression "d"
}
1
2
3
4
5
6
7
8
9

执行代码期间会修改变量对象的属性值

# 思考

console.log(foo);

function foo() {
  console.log("foo");
}

var foo = 1;
1
2
3
4
5
6
7
 AO = {
   foo: referenceto function foo() {}
 }

 // 开始执行代码,先执行 console.log(foo) 所以会打印 函数
 // 再往下执行,foo 被赋值为 1
 // 补充: 这种属于全局变量对象,没有函数的形参
1
2
3
4
5
6
7

进入执行上下文,会首先处理函数声明,其次处理变量声明,如果变量名跟已经声明的形参或函数名相同,则不会有干扰。 因此会打印 函数 function foo () {}

# 个人总结

执行代码片段,先创建执行上下文,生成 变量对象(VO),此时上下文中的函数和变量不可访问, 进入执行上下文,激活变量对象为活动独享(AO),此时可以访问其中的函数和变量 按顺序执行代码,并且重新给 AO 中的变量或函数进行赋值操作。