变量对象(VO)和活动对象(AO)本质上是同一对象在执行上下文不同阶段的两种状态:创建阶段称VO,执行阶段称AO;VO是待命的静态结构,AO是已赋值、可访问的活体,全局上下文无AO切换。

变量对象(VO)和活动对象(AO)本质上是同一个对象在执行上下文不同阶段的两种状态:创建阶段叫变量对象,执行阶段叫活动对象。它们不是两个独立对象,而是同一内存实体在生命周期中的角色切换。
变量对象:执行前的“蓝图”
在函数被调用、但尚未执行任何语句时,JavaScript 引擎进入执行上下文创建阶段,此时构建变量对象:
- 收集所有 函数声明(FunctionDeclaration),以函数名为属性名,值为函数引用;同名函数声明会覆盖之前的
- 收集所有 var 变量声明,以变量名为属性名,值统一设为
undefined;若与已有函数或形参同名,则跳过,不覆盖 - 初始化 arguments 对象(仅函数上下文),包含实参值的类数组对象
- 形参列表也作为属性写入 VO,值为传入的实际参数(未传则为
undefined)
此时 VO 已存在,但所有属性还不能被代码访问——它只是待命的“静态结构”。
活动对象:执行时的“活体”
当上下文进入执行阶段(即开始逐行运行函数体代码),VO 就自动“激活”,变成活动对象(AO):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- AO 与 VO 是同一对象,只是名称随阶段变化;引擎内部不新建对象,只改变可访问性
- 此时变量真正被赋值:
var a = 10这一行会让 AO.a 从undefined变为10 -
this、arguments和命名参数也在此阶段完成绑定并可被访问 - 只有位于执行栈顶的函数上下文,其 VO 才会成为 AO;其他挂起的上下文仍保持 VO 状态
全局上下文是个例外
全局执行上下文没有“激活”过程,它的变量对象就是全局对象本身(浏览器中是 window 或 globalThis):
-
var a = 1直接在全局对象上添加a属性 - 函数声明
function fn(){}同样直接挂到全局对象上 - 因此全局 VO 始终可访问,不需要“转为 AO”这一步
为什么这个区分很重要
理解 VO/AO 的切换,才能解释常见现象:
- 变量提升(Hoisting):因为 var 和 function 在创建阶段就已写入 VO,所以能在声明前访问(函数可调,var 为 undefined)
- 重复声明无报错:VO 阶段对同名 var 会跳过,对同名 function 会覆盖,这是语法层设计,不是运行时错误
- 闭包中保存的是 AO 状态:内层函数引用外层变量时,捕获的是外层上下文的 AO(即已赋值后的最终状态),而非创建时的 VO

















