定义
变量对象 (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 = 10
var b = 20
c = 30
function 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 = 10
console.log(x) // 10
x = 20
function 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