变量对象是一个与执行上下文相关的特殊对象,存储着上下文中声明的内容。按照执行上下文可划分为全局上下文中的变量对象和函数上下文中的对象。
- 全局上下文中变量对象
全局对象在进入全局执行上下文之前就已经完成创建,该对象只有一份,其属性可在程序任何位置访问,其生命周期将在程序退出时终止。例如定义在全局的变量、Math、Date等都属于全局上下文中的变量对象。 - 函数上下文中的变量对象
在函数执行上下文中的存在着与该执行上下文相关的内容:形参、函数声明、变量,这就是函数中的变量对象。(下文将重点介绍函数中的变量对象)
在“百题斩【10】——执行上下文”中讲述了执行上下文分为创建阶段和代码执行阶段,其中创建阶段中会完成变量创建;在代码执行阶段会完成变量赋值。下面从这两个阶段介绍该过程。
function test(x, y, z) { console.log(arguments); console.log(childFun()); console.log(val1); var val1 = 10; console.log(val1); function childFun() { return 'childFun'; } console.log(childFun()); }
11.1 创建阶段
在变量对象的创建过程中,分别完成以下几个步骤:
- 创建arguments对象
arguments
是一个对应于传递给函数的参数的类数组对象。在变量对象创建阶段,首先检查当前上下文中的参数,建立该对象下的属性与属性值。arguments
的创建后的结果如下所示:
[Arguments] { '0': x, '1': y, '2': z }
- 检查
function
函数声明创建属性
在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
- 检查变量声明创建属性
对于var声明的变量,每找到一个就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。对于let和const声明的变量只会建立一个属性但并没有对齐进行赋值,所以不能进行使用。
11.2 执行阶段
未进入执行阶段之前,变量对象中的属性都不能访问,但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。(变量对象和活动对象其实都是同一个对象,只是处于执行上下文的不同生命周期)
首先看一下整个代码的执行结果
function test1(x, y, z) { console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3 } console.log(childFun()); // childFun console.log(val1); // undefined var val1 = 10; console.log(val1); // 10 function childFun() { return 'childFun'; } console.log(childFun()); // childFun } test1(1, 2, 3);
- 上面中的
arguments
接收到了传入的参数,完成了对应属性值的赋值过程,并验证了arguments
参数的结构;
- childFun函数在两个位置均可以调用输出正确的执行结果,证明在创建阶段函数得到了提升并完成了对应的赋值;
- val1变量在初始化之前可以进行调用,则证明在其创建阶段完成了变量提升,但是其值只被赋值为undefined,当执行到对应语句才正确赋值。