JavaScript 执行上下文与变量对象深度解析
本文深入解析 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 程序设计有所帮助。
D3.js 比例尺与坐标轴的配置详解
2026-11-05 16:54:46
JavaScript怎么遍历对象的Symbol键和不可枚举属性
2026-11-05 13:13:39
封装一个健壮的JS字符串判空工具函数
2026-11-05 12:16:14
JavaScript合并数组的三种常用方法:concat、扩展运算符与push
2026-11-05 11:22:29
JavaScript立即执行函数(IIFE)是什么及基本语法
2026-11-05 10:51:19
