变量对象

变量对象(Variable Object,VO)用于记录在当前作用域中可以访问到的所有变量。

变量对象的创建

在执行上下文的创建阶段,除了执行上下文自身被创建外,同时其内部会分别创建变量对象(VO)、建立作用域链(SC),以及确定 this 的指向(之前确定好的作用域也会添加进来)。

而变量对象在创建过程中也分为了三个阶段:

  1. 检查当前作用域中的函数参数:建立 arguments 对象,将形参和实参作为该对象下的属性名与属性值。
  2. 检查当前作用域中的声明式函数:以函数名建立一个属性,属性值为该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
  3. 检查当前作用域中var声明的变量:以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

图解分析:

代码分析:

1
2
3
4
5
6
7
8
9
10
11
function outer(num){
console.log(num);
console.log(a);
console.log(inner());
var a = 1;
function inner(){
return 2;
}
}
outer(10);

在全局代码outer(10)执行时,outer 函数被调用,产生局部上下文,同时其内部的变量对象也开始创建。为了便于理解,我们通过伪代码的形式来表示一下:

1
2
3
4
5
6
7
outerEC = { // outer 的执行上下文
outerVO: { // outer 的变量对象
arguments: {num: 10}, // 参数
inner: 地址, // 声明式函数
a: undefined // var 声明的变量
}
}

活动对象

执行上下文未进入“代码执行阶段”前,变量对象中的属性都不能访问。一旦进入“代码执行阶段”,变量对象就会转换为活动对象(Active Object,AO),而活动对象中的属性就可以进行访问了。

说明:变量对象和活动对象其实就是同一个对象,只是处于执行上下文不同的阶段有不同的名字。

因此,当上面的例子进入“代码执行阶段”后,变量对象转换为活动对象,伪代码如下所示:

1
2
3
4
5
6
7
outerEC = {
outerAO: {
arguments: {num: 10},
inner: 地址,
a: 1 // 变量 a 被赋值为 1
}
}

因此,当console.log()语句执行时,就会去变量对象中查询对应的数据并输出。其实”变量提升”和”函数提升”这两个概念的本质就是变量对象的创建过程。

总结

  1. 变量对象是在执行上下文建立阶段产生的。
  2. 变量对象的建立分为了三个过程:查找参数、查找声明式函数、查找var变量。
  3. 变量对象中的属性不能进行任何操作。
  4. 变量对象在执行上下文激活阶段时变为活动对象,其内部属性才可以进行操作。
我 秦始皇 打钱