定义
变量对象 (variable object, AO)
活动对象 (activation object,VO)
在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。
活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object ,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。
∵ VO = 变量声明 + 函数声明
∵ AO = AO + arguments(实参) + formal parameters(形参)
∴ AO = 变量声明 + 函数声明 + arguments(实参) + formal parameters(形参) // 函数变量对象
活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。
ECMAScript规范指出独立作用域只能通过“函数(function)”代码类型的执行上下文创建。
变量对象
变量对象(VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:
- 变量声明
- 函数声明
- 函数的形参
全局上下文中的变量对象就是全局对象
全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象。这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。
在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。
活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在
JavaScript环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫activation object,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。只有全局上下文的变量对象允许通过VO的属性名称来间接访问(因为在全局上下文里,全局对象自身就是变量对象),在其它上下文中是不能直接访问VO对象的,因为它只是内部机制的一个实现。
如下代码:
var a = 10;function fn(args) {var b = 20;};test(20);
对应的变量对象如下:
VO(globalContext) = {a: 10,fn: <reference function>}VO(fnContext) = {args: 20,b: 20}
函数执行上下文生命同期
在函数执行上下文中,VO是不能直接访问的,由AO代表VO出现。
执行上下文的代码会分成两个阶段进行处理:创建、执行、回收。
- 创建阶段
- 执行阶段
- 回收阶段
以如下代码为例说明:
function fn(x, y, z) {var a = 10var b = 20c = 30function b() {}var sum = function() {}}fn(11, 22)
创建阶段
创建变量对象VO,并进行以下动作进行初始化:
- 处理函数的形参
- 用
arguments初始化变量对象属性,arguments属性的值是Arguments对象。 - 没有实参,对应的形参的值是
undefined。
- 用
- 函数声明
- 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建。
- 如变量对象已经存在相同名称的属性,则完全替换这个属性。
- 变量声明
- 由名称和对应值(undefined)组成一个变量对象的属性被创建。
- 如变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
到此,VO 变量对象如下:
VO(fn) = {arguments: {0: 11,1: 22,length: 2},x: 11,y: 22,z: undefined,a: undefined,b: <reference function b>,sum: undefined}
执行阶段
进入执行阶段后,VO 变 AO,且 AO 可访问,在执行行对 AO 进行改变,如赋值操作等。
此时 AO 如下:
AO(fn) = {arguments: {0: 11,1: 22,length: 2},x: 11,y: 22,z: undefined,a: 10, // 在执行到“var a = 10”时,修改该属性b: <reference function b>,sum: <reference function sum> // 同上}
再来看一段代码:
function fn() {console.log(x) // function x() {}var x = 10console.log(x) // 10x = 20function x() {}console.log(x) // 20}
解析:
- 在
AO中因为函数声明优先于变量声明,得VO(fn).x = function() {}所以第一个x是function x() {} - 接着执行得
VO(fn).x = 10,所以第二个x得10 - 继续执行得
VO(fn).x = 10,所以第三个x得20
