VO与AO是同一内部对象在不同阶段的称呼:VO是创建阶段的初始化蓝图,AO是执行阶段的可操作实例,二者为状态升级而非复制或新对象生成。

函数执行上下文中的活动对象(AO)和变量对象(VO)本质上是同一个内部对象在不同阶段的称呼:VO 是创建阶段的抽象容器,AO 是执行阶段的可操作实例。理解它们的关键,在于把握“阶段变化”和“行为差异”,而不是把它们当成两个独立对象。
变量对象(VO):创建阶段的初始化蓝图
当函数被调用、执行上下文刚被创建(但代码尚未执行)时,JavaScript 引擎会构建一个 VO。它不暴露给开发者,仅存在于引擎内部,作用是为后续执行提前登记所有声明项:
-
形参:按实参传入值;未传则设为
undefined - 函数声明(FunctionDeclaration):直接挂载函数引用,同名时会覆盖已有属性(包括形参或 var 变量)
-
var 变量声明:仅声明,值统一初始化为
undefined;若与形参或函数同名,则跳过,不覆盖 - let/const 不参与 VO 初始化:它们受暂时性死区(TDZ)约束,由词法环境(LexicalEnvironment)单独管理
活动对象(AO):执行阶段的真实载体
一旦进入代码执行阶段,VO 就“激活”为 AO——它仍是那个对象,但此时所有属性都可读写,且承载真实值:
- AO 中的属性可被函数体内的代码直接访问和修改(如
a = 10会更新AO.a) -
arguments对象成为 AO 的真实属性,在非严格模式下,其索引属性(如arguments[0])与对应形参变量共享存储位置 - 函数表达式(
var d = function() {})的赋值发生在执行阶段,因此 AO 初始为undefined,执行到该行才变为函数引用 - Ao 生命周期与执行上下文一致:入栈即创建,出栈即销毁
VO → AO 的转换不是复制,而是状态升级
这个过程没有新对象生成,也没有属性拷贝。VO 和 AO 指向内存中同一结构,区别仅在于阶段权限:
- 创建阶段(VO):属性已存在,但不可访问(如直接
console.log(a)会报错或返回undefined,取决于是否被提升) - 执行阶段(AO):属性开放读写,变量赋值、函数调用、
arguments使用全部生效 - 全局上下文例外:它的 VO 就是全局对象(如浏览器中为
window),可直接访问,无需“激活”
典型例子看清楚阶段差异
以这段代码为例:
function foo(a) {console.log(a); // 输出 1(AO 阶段,a 已有值)
var a = 2;
console.log(a); // 输出 2
}
创建阶段 VO = { a: 1 }(形参传入);
执行阶段 AO 初始同 VO,第一行 console.log(a) 能读到 1,说明它已是可访问的 AO;
第二行 var a = 2 实际是给 AO.a 赋新值,不是重新声明。


















