一、执行上下文(Execution Context)的核心概念
执行上下文 是 JavaScript 代码执行时的环境容器,它决定了变量、函数、作用域链和 this 的绑定关系。每个函数调用、全局脚本执行都会创建一个新的执行上下文。
二、执行上下文的类型
| 类型 | 创建时机 | 特点 |
|---|---|---|
| 全局执行上下文 | 脚本首次加载时 | 唯一且持续到页面关闭,this 指向全局对象(浏览器中为 window) |
| 函数执行上下文 | 每次函数调用时 | 每次调用都会创建新上下文,this 由调用方式决定(如 call、apply) |
| Eval 执行上下文 | eval() 内部代码执行时 | 极少使用,存在安全风险 |
三、执行上下文的生命周期
分为 创建阶段 和 执行阶段,具体流程如下:
1. 创建阶段(Creation Phase)
-
步骤1:绑定
this值- 全局上下文:
this指向全局对象(如window)。 - 函数上下文:
this由调用方式决定(默认指向全局对象,严格模式为undefined)。
- 全局上下文:
-
步骤2:创建词法环境(Lexical Environment)
- 环境记录(Environment Record):存储变量、函数、参数声明。
- 声明式环境记录:处理
let、const、函数声明(块级作用域)。 - 对象式环境记录:处理
var声明(全局或函数作用域)。
- 声明式环境记录:处理
- 外部环境引用(Outer Reference):指向父级词法环境,形成作用域链。
- 环境记录(Environment Record):存储变量、函数、参数声明。
-
步骤3:创建变量环境(Variable Environment)
- 在 ES6 之前,变量环境与词法环境是同一个。
- ES6 后,变量环境专门处理
var声明,而词法环境处理let、const和块级作用域。
2. 执行阶段(Execution Phase)
- 逐行执行代码,进行变量赋值、函数调用等操作。
- 此时会处理暂时性死区(TDZ)的限制(
let/const声明前不可访问)。
四、执行上下文栈(调用栈)
JavaScript 使用 后进先出(LIFO) 的栈结构管理执行上下文,称为 调用栈(Call Stack)。
调用栈示例
function outer() {
console.log("outer");
inner();
}
function inner() {
console.log("inner");
}
outer();
console.log("global");
调用栈流程:
- 全局上下文入栈 → 执行
outer()→outer上下文入栈。 outer中调用inner()→inner上下文入栈。inner执行完毕 → 出栈 → 回到outer。outer执行完毕 → 出栈 → 回到全局上下文。- 执行
console.log("global")→ 全局上下文最后出栈。
五、变量环境 vs 词法环境
| 特性 | 变量环境(Variable Environment) | 词法环境(Lexical Environment) |
|---|---|---|
| 存储内容 | var 声明、函数声明 | let、const 声明、块级作用域变量 |
| 作用域管理 | 函数作用域或全局作用域 | 块级作用域 |
| 变量提升 | var 声明提升,初始化为 undefined | let/const 声明提升,但存在 TDZ |
| ES6+ 支持 | 兼容旧代码 | 支持新特性(如块级作用域) |
六、执行上下文与闭包的关系
闭包是函数与其定义时的词法环境的结合。即使外部函数执行完毕,其执行上下文从栈中弹出,但闭包仍保留对外部变量环境的引用。
闭包示例
function createCounter() {
let count = 0; // 被闭包保留的变量
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
执行上下文与闭包的关系:
createCounter执行时,其执行上下文入栈,创建变量count。- 返回的内部函数在定义时记录了
createCounter的词法环境。 - 即使
createCounter执行完毕出栈,内部函数仍通过作用域链访问count。
七、变量提升的本质
变量提升发生在执行上下文的 创建阶段,具体行为因声明方式而异:
1. var 的变量提升
console.log(a); // undefined
var a = 10;
- 创建阶段:
a被声明并初始化为undefined。 - 执行阶段:
a被赋值为10。
2. let/const 的暂时性死区(TDZ)
console.log(b); // ReferenceError
let b = 20;
- 创建阶段:
b被声明但未初始化(处于 TDZ)。 - 执行阶段:执行到
let b = 20时,b才被初始化。
八、执行上下文与作用域链的联动
作用域链由执行上下文的词法环境的外部引用(outer)串联而成,决定变量查找路径。
作用域链示例
const globalVar = "global";
function outer() {
const outerVar = "outer";
function inner() {
console.log(outerVar); // 通过作用域链查找
console.log(globalVar);
}
inner();
}
outer();
作用域链结构:
inner 词法环境 → outer 词法环境 → 全局词法环境
九、最佳实践与调试技巧
- 避免滥用闭包:防止内存泄漏,及时解除不再使用的引用。
- 优先使用
let/const:明确变量作用域,减少意外污染。 - 利用 DevTools 调试作用域:在 Chrome 开发者工具的 Sources 面板中,通过断点查看 Scope 面板的作用域链结构。
十、总结
- 执行上下文 是代码执行的基础环境,包含变量环境、词法环境和
this绑定。 - 调用栈 管理上下文的执行顺序,遵循后进先出原则。
- 作用域链 通过词法环境的
outer引用实现变量逐级查找。 - 闭包 是函数与其定义时词法环境的结合,通过保留作用域链实现跨作用域访问。
JavaScript执行上下文深度剖析

被折叠的 条评论
为什么被折叠?



