JavaScript执行上下文分全局、函数、eval三种,仅在全局执行、函数调用、eval执行时创建;每个上下文含VariableEnvironment、LexicalEnvironment和this绑定三部分。

JavaScript 的执行上下文不是抽象概念,而是真实存在的、可推导的运行时结构——它决定了变量和函数在哪儿能被访问、值是什么、this 指向谁。理解它,关键不是背定义,而是看代码执行时“引擎到底做了什么”。
执行上下文分哪几种?什么时候创建?
JS 引擎只在三种时刻新建执行上下文:
- 全局代码开始执行时 → 创建
Global Execution Context - 调用一个函数时 → 创建新的
Function Execution Context - 执行
eval()(不推荐)时 → 创建Eval Execution Context
注意:if、for、{} 块级作用域不会创建执行上下文,它们只影响词法环境(Lexical Environment),但不触发上下文栈入栈。
每个执行上下文内部有哪三块核心数据?
每个上下文都包含三个关键部分,且按固定顺序初始化:
立即学习“Java免费学习笔记(深入)”;
-
VariableEnvironment:存放var声明、函数声明(会被提升)、function参数 —— 这是真正参与变量提升(hoisting)的部分 -
LexicalEnvironment:存放let、const、class声明(暂存性死区 TDZ 就发生在这里);在函数中,它通常初始时与VariableEnvironment相同,但后续可能分离(比如遇到with或catch绑定) -
this绑定:由调用方式决定,不是由声明位置决定 ——obj.fn()中this是obj,而fn()独立调用时在非严格模式下是window(或globalThis),严格模式下是undefined
为什么 setTimeout 里的函数总拿不到最新值?跟执行上下文有关吗?
有关,但不是因为“闭包捕获了旧上下文”,而是因为:每次循环迭代都会创建**新的词法环境**(LexicalEnvironment),而 setTimeout 回调在将来执行时,会通过作用域链向上查找——它找的是自己被定义时所在那次迭代的环境,而不是最后一次。
这是一套由淘掌门(taozhangmen.net)衍生出来的一个拍拍客系统!这套程序也继承了淘掌门的特点:永久免费开源!无任何时间限制、功能限制、域名限制。 程序相对于淘掌门原型,已去除返利、会员系统、文章系统等。 如果需要文章,可单独下载其他的文章系统,做子目录,效果可能会更好。 程序安装过程与淘掌门相同: 下载上传到空间,执行 你的网址/install.php 安装完成后,登陆后台修改拍拍AP
典型例子:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 输出 3, 3, 3
}
原因:var 声明绑定在 VariableEnvironment 上,整个循环共用一个 i;而改成 let 就输出 0,1,2,因为 let 每次迭代都生成新绑定,对应新 LexicalEnvironment。
调试时怎么看当前执行上下文?
浏览器 DevTools 不直接显示“执行上下文对象”,但你可以间接观察它的行为:
- 断点停住后,在 “Scope” 面板里看到的 “Closure”、“Script”、“Global” 就是当前上下文的
LexicalEnvironment链 - 展开 “Local” 可见当前函数的参数和
let/const变量;“Script” 显示当前脚本级的var和函数声明 - 右键变量 → “Re-evaluate in console”,可验证该变量是否处于 TDZ(报
ReferenceError: Cannot access 'x' before initialization)
真正的难点不在识别上下文存在,而在于意识到:函数体内的自由变量,查的是它**定义时**所处的词法环境链,不是**执行时**所处的调用栈——这个错位,是绝大多数闭包误解和 this 失控的根源。










