JavaScript 执行上下文与变量对象深度解析

作者:异步漫步

时间:2026-10-23

浏览:152

本文深入解析 JavaScript 的执行上下文与变量对象机制。详细阐述执行上下文的创建过程、堆栈结构以及变量对象(VO)在声明阶段与执行阶段的行为差异,并通过实例说明变量声明、函数声明及形参在 VO 中的存储规则,澄清关于变量声明的常见误区。

文章目录

本文通过具体实例解析 JavaScript 的执行上下文与变量对象概念,旨在为读者提供清晰的参考。

回顾一篇发布于 2010 年的经典文章,作者以章节化方式介绍了 ECMA-262-3 标准中关于执行上下文、变量对象、作用域及 this 指向等核心语言细节。该文篇幅精炼且逻辑严谨,阅读体验极佳,值得深入研读。

原文链接:这里

原本计划进行翻译,后发现已有他人完成译作,此处略过。

记录此笔记,以作备忘。

执行上下文 ExecutionContext

当程序控制权转移至 ECMAScript 可执行代码时,系统会创建并进入一个新的执行上下文。

这一简短描述背后蕴含了丰富的运行时机制:

  • 控制器:即 js 引擎
  • 转换:指从一段可执行代码跳转到另一段可执行代码的过程
  • 可执行代码:包括全局代码、函数代码以及 eval 代码(分别对应三种不同的作用域类型)
  • 执行上下文:这是一个抽象概念,ECMA-262 标准利用此概念来区分不同类型的可执行代码(executable code)。

在逻辑上,执行上下文构成一个堆栈结构。堆栈底部始终固定为全局上下文(global context),而堆栈顶部则指向当前活动或正在执行的执行上下文(activeExecutionContext)。随着执行上下文变量(various kinds of EC)被压入或弹出堆栈,该堆栈结构随之动态变化。

例如,我们可以定义一个数组来模拟执行上下文堆栈的状态:


ECStack = [
 globalContext,
 <foo> functionContext
]

变量对象 VariableObject

变量对象(VO)作为执行上下文的一个属性存在,用于存储以下三类内容:

  • 所有变量声明 (var, VariableDeclaration)
    • VO 包含一个属性,该属性由变量名称和 undefined 值组成;若变量名称与已声明的形式参数或函数名相同,则新的变量声明不会覆盖已存在的此类属性。
  • 函数声明 (FunctionDeclaration, 缩写为FD)
    • VO 包含一个属性,该属性由函数对象(function-object)的名称和值组成;若 VO 中已存在同名属性,则新声明的函数将完全替换该属性。
  • 以及函数的形参
    • VO 包含一个属性,该属性由形式参数的名称和值组成;若未传递实际参数,则该属性由形式参数名称和 undefined 值组成。

VO = {
  // context data (var, FD, function arguments)
 }

在声明变量或函数时,系统会同时以变量名称和值在 VO 中创建一个新的属性。 例如:


var m = 30;
function test(a,b) {
 var c = 20
 function d() {}
 var e = function _e() {};
}
test(10)

当进入“test”函数的执行上下文(并传递参数 10)时,活动对象(AO)的状态如下:


AO(test) = {
 a: 10,
 b: undefined,
 c: undefined,
 d: <reference to FunctionDeclaration "d">
 e: undefined
};

当“test”函数执行结束时,对应的上下文堆栈状态为:


ECStack = [
 globalContext: {
  VO: {
   m: 30,
   test: 
  }
 },
 test functionContext: {
  VO: {
   a: 10,
   b: undefined,
   c: 20,
   d: <reference to FunctionDeclaration "d">,
   e: <reference to FunctionDeclaration "_e">
  }
 }
]

关于变量

许多 JavaScript 书籍和文章声称:“无论是否使用 var 关键字(全局或局部),都可以声明变量”。这是一个绝对的误解:在任何情况下,变量都必须通过 var 关键字才能正确声明。

通过以下实例观察具体区别:


alert(a); // undefined
alert(b); // "b" is not defined
 
b = 10;
var a = 20;

根本原因仍在于 VO 及其修改阶段(即进入上下文阶段与执行代码阶段):


VO = {
 a: undefined
};

可以看到,由于“b”未被正确声明为变量,在此阶段根本不存在“b”,它仅在执行代码阶段才会出现(但在本例中,程序在到达该阶段前已报错)。

调整示例代码后:


alert(a); // undefined, we know why
 
b = 10;
alert(b); // 10, created at code execution
 
var a = 20;
alert(a); // 20, modified at code execution

关于变量,还有一个关键知识点:与简单属性不同,变量具有一个名为 {DontDelete} 的特性(attribute),这意味着无法通过 delete 操作符直接删除变量属性。


a = 10;
alert(window.a); // 10
 
alert(delete a); // true
 
alert(window.a); // undefined
 
var b = 20;
alert(window.b); // 20
 
alert(delete b); // false
 
alert(window.b); // still 20

2018-8-2-再看执行上下文、变量对象

感兴趣的朋友可以使用在线 HTML/CSS/JavaScript 代码运行工具:http://tools.jb51.net/code/HtmlJsRun 测试上述代码的运行效果。

更多关于 JavaScript 相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家 JavaScript 程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章