835 字
4 分钟
JavaScript 执行上下文 & 执行上下文栈
.png)
核心定义
执行上下文(EC) 是 JavaScript 代码执行的环境,包含:
- 变量对象(VO → AO)
- 作用域链(Scope Chain)
this
绑定 执行上下文栈(ECS) 是 LIFO 栈结构,用于管理代码执行期间的 EC 顺序:
- 全局 EC 首先入栈
- 函数调用时新 EC 入栈
- 函数执行结束 EC 出栈
工作原理
- 创建阶段:
- 创建变量对象(函数参数/函数声明/变量声明)
- 建立作用域链(父级 VO + 当前 VO)
- 确定
this
值
- 执行阶段:
- 变量赋值
- 函数调用
- 代码执行
- 销毁阶段:
- 执行完毕的 EC 从栈中弹出
flowchart TD A[代码执行] --> B[创建全局执行上下文 GEC] B --> C[GEC 入栈] D[函数调用] --> E[创建函数执行上下文 FEC] E --> F[FEC 入栈] F --> G{函数执行完成?} G -->|是| H[FEC 出栈] G -->|否| I[继续执行] H --> J[回到栈顶上下文]
关键点
- 上下文类型:全局/函数/eval(不推荐)
- 变量对象:
- 全局:全局对象(浏览器中为
window
) - 函数:活动对象(AO),含参数/局部变量
- 全局:全局对象(浏览器中为
- 作用域链:
- 保证变量访问权限
- 闭包保留外部作用域引用
this
绑定:- 全局:全局对象
- 函数:取决于调用方式
- 栈大小限制:递归过深导致栈溢出
常见误区
- 混淆作用域链与原型链:作用域链处理变量访问,原型链处理属性查找
- 误解
this
:认为this
指向函数自身或 EC 对象(实际由调用方式决定) - 闭包内存泄漏:未释放外部变量的闭包(如事件回调)
- 变量提升误判:忽略 EC 创建阶段的变量/函数声明提升
- 递归爆栈:未优化尾递归导致栈溢出
应用场景
场景 | 执行上下文作用 | 栈行为 |
---|---|---|
全局代码执行 | 创建全局 VO(window ) | GEC 始终在栈底 |
函数调用 | 创建函数 AO(含参数/局部变量) | FEC 入栈 → 执行 → 出栈 |
闭包 | 保留外部函数 AO 的引用 | 外部 FEC 出栈后 AO 仍被闭包引用 |
递归函数 | 每次递归创建新 FEC | 递归过深导致栈溢出 |
构造函数调用 | 创建新对象并绑定 this | 新 FEC 入栈,this 指向实例 |
事件处理 | 创建新的 FEC(this 绑定事件目标) | 回调入栈执行 |
关联知识
- 变量提升(Hoisting):EC 创建阶段的变量/函数声明初始化
- 闭包(Closures):函数保留其声明时作用域链的引用
this
绑定规则:- 默认绑定:
window
(非严格模式) - 隐式绑定:对象方法调用
- 显式绑定:
call
/apply
/bind
new
绑定:构造函数
- 默认绑定:
- 作用域(Scope):词法作用域(静态) vs 动态作用域
- 内存管理:
- 闭包导致的外部变量内存不释放
- 栈内存自动回收(EC 出栈时)
- 堆内存通过 GC 回收
- 事件循环(Event Loop):
- 执行栈清空后处理微任务/宏任务
- 任务队列触发新 EC 创建
💡 黄金法则:
- 每次函数调用 → 新建 EC → 入栈
- 函数执行完毕 → EC 出栈 → 销毁
- 栈底永远是全局 EC
- 闭包 = 函数 + 声明时的作用域链引用 使用 Chrome DevTools 的 Call Stack 面板直观查看执行栈!
JavaScript 执行上下文 & 执行上下文栈
https://website-truelovings-projects.vercel.app/posts/frontend/javascript/javascript-执行上下文--执行上下文栈/