在 JavaScript 中,执行上下文(Execution Context) 是代码执行的“运行环境”。它决定了变量如何查找、this 如何绑定、函数如何调用。理解执行上下文,是掌握 JS 引擎工作原理、闭包、作用域链、this 指向等核心概念的关键。
本文将系统性地解析 执行上下文的类型、执行栈、创建过程(词法环境与变量环境),帮你构建完整的 JS 执行模型。
一、什么是执行上下文?
执行上下文是 JavaScript 引擎为当前正在执行的代码块创建的一个抽象环境,它包含了代码执行所需的所有信息。
你可以把它理解为:
“一个函数或一段代码在运行时的‘工作台’,上面摆放着它的变量、函数、
this等资源。”
二、执行上下文的三种类型
✅ 1)全局执行上下文(Global Execution Context)
- 触发时机:JavaScript 程序启动时自动创建;
- 数量:整个程序只有一个全局上下文;
- 创建内容:
- 创建全局对象(浏览器中是
window); - 将
this绑定到全局对象; - 解析全局代码,进行变量提升和函数提升。
- 创建全局对象(浏览器中是
// 全局上下文
let globalVar = 'I am global';
function globalFunc() { }
✅ 2)函数执行上下文(Function Execution Context)
- 触发时机:每次函数被调用时都会创建一个新的函数上下文;
- 数量:可以有任意多个,取决于函数调用次数;
- 创建内容:
- 创建
arguments对象(非箭头函数); - 确定
this的值; - 解析函数内部的变量和函数声明。
- 创建
function foo() {
// 每次调用 foo() 都会创建一个新的函数执行上下文
let localVar = 'I am local';
}
foo(); // 第1个上下文
foo(); // 第2个上下文
✅ 3)eval 执行上下文(Eval Execution Context)
- 在
eval()函数中执行的代码会创建自己的执行上下文; - 由于
eval存在安全风险且性能较差,不推荐使用,此处不做深入讨论。
三、执行上下文栈:代码执行的“调用顺序”
✅ 什么是执行上下文栈(Call Stack)?
JavaScript 引擎使用一个栈结构(后进先出,LIFO)来管理执行上下文的创建和销毁。
🔧 工作流程:
- 程序启动 → 创建全局执行上下文 → 压入栈底;
- 遇到函数调用 → 创建函数执行上下文 → 压入栈顶;
- 执行栈顶的上下文;
- 函数执行完毕 → 从栈中弹出该上下文;
- 继续执行下一个上下文;
- 所有代码执行完毕 → 弹出全局上下文,程序结束。
🔍 示例分析
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
执行过程与栈的变化:
| 步骤 | 栈状态(从底到顶) | 当前执行 |
|---|---|---|
| 1 | [全局] | 全局代码 |
| 2 | [全局, first] | first() 开始 |
| 3 | [全局, first, second] | second() 开始 |
| 4 | [全局, first] | second() 结束 |
| 5 | [全局] | first() 结束 |
| 6 | [] | 程序结束 |
📌 输出顺序:
Inside first function
Inside second function
Again inside first function
❌ 你提到的“先执行 second,再执行 first”是不准确的。实际上是
first先开始执行,然后调用second,second执行完后继续执行first的剩余代码。
四、执行上下文的创建阶段:预解析的秘密
创建执行上下文分为两个阶段:
✅ 阶段1:创建阶段(Creation Phase)
在代码执行前,JS 引擎会进行“预解析”,创建上下文的结构。
(1)this 绑定
| 上下文类型 | this 指向 |
|---|---|
| 全局上下文 | window(浏览器)或 global(Node.js) |
| 函数上下文 | 取决于调用方式: • obj.func() → obj• func() → window(非严格模式)或 undefined(严格模式) |
(2)创建词法环境(Lexical Environment)
- 定义:用于存储函数声明和块级作用域变量(
let/const)的映射; - 结构:
- 环境记录器(Environment Record):存储变量和函数声明;
- 外部环境引用(Outer Environment Reference):指向外层作用域,形成作用域链。
(3)创建变量环境(Variable Environment)
- 定义:用于存储
var声明的变量; - 本质:也是一个词法环境,但在 ES6 中被分离出来,主要用于处理
var的变量提升。
📌 简单理解:
词法环境:管理let/const函数声明;变量环境:管理var声明;- 两者都参与作用域链的构建。
✅ 阶段2:执行阶段(Execution Phase)
- 变量赋值(将
var变量从undefined赋为实际值); - 函数执行;
- 逐行执行代码。
五、图解:执行上下文的内部结构
函数执行上下文 {
LexicalEnvironment: {
EnvironmentRecord: {
a: <uninitialized>, // let/const 声明(暂时性死区)
func: function() {}
},
Outer: <全局词法环境>
},
VariableEnvironment: {
EnvironmentRecord: {
b: undefined // var 声明(变量提升)
},
Outer: <全局词法环境>
},
ThisBinding: window // this 的值
}
六、经典案例:变量提升与执行顺序
console.log(a); // undefined(来自变量环境)
console.log(b); // 报错!Cannot access 'b' before initialization(暂时性死区)
var a = 1;
let b = 2;
📌 原因:
var a在变量环境中被提升,初始值为undefined;let b在词法环境中被声明,但未初始化,访问会报错。
七、总结:执行上下文核心要点
| 概念 | 关键点 |
|---|---|
| 全局上下文 | 唯一,程序启动时创建,this 指向 window |
| 函数上下文 | 每次调用创建,包含 this、arguments |
| 执行栈 | 后进先出,管理上下文的执行顺序 |
| 创建阶段 | this 绑定 + 词法环境 + 变量环境 |
| 词法环境 | 管理 let/const/函数声明 |
| 变量环境 | 管理 var 声明(变量提升) |
| 执行阶段 | 变量赋值,代码执行 |
💡 结语
执行上下文是 JavaScript 的‘运行时宇宙’,所有代码都在其中诞生、运行、消亡。
理解执行上下文的创建与执行过程,能让你真正明白:
- 为什么变量会“提升”?
- 为什么
let有“暂时性死区”? this到底是谁?- 闭包是如何“记住”外部变量的?
这些都不是魔法,而是执行上下文机制的自然结果。
📌 建议:动手画一画执行栈和上下文结构,多调试 console.log,你会对 JS 的执行模型有更深的体会。