835 字
4 分钟
JavaScript 执行上下文 & 执行上下文栈

核心定义#

执行上下文(EC) 是 JavaScript 代码执行的环境,包含:

  1. 变量对象(VO → AO)
  2. 作用域链(Scope Chain)
  3. this 绑定 执行上下文栈(ECS) 是 LIFO 栈结构,用于管理代码执行期间的 EC 顺序:
  • 全局 EC 首先入栈
  • 函数调用时新 EC 入栈
  • 函数执行结束 EC 出栈

工作原理#

  1. 创建阶段
    • 创建变量对象(函数参数/函数声明/变量声明)
    • 建立作用域链(父级 VO + 当前 VO)
    • 确定 this 值
  2. 执行阶段
    • 变量赋值
    • 函数调用
    • 代码执行
  3. 销毁阶段
    • 执行完毕的 EC 从栈中弹出
flowchart TD 
	A[代码执行] --> B[创建全局执行上下文 GEC] 
	B --> C[GEC 入栈] 
	D[函数调用] --> E[创建函数执行上下文 FEC] 
	E --> F[FEC 入栈] 
	F --> G{函数执行完成?} 
	G -->|是| H[FEC 出栈] 
	G -->|否| I[继续执行] 
	H --> J[回到栈顶上下文]

关键点#

  1. 上下文类型:全局/函数/eval(不推荐)
  2. 变量对象
    • 全局:全局对象(浏览器中为 window
    • 函数:活动对象(AO),含参数/局部变量
  3. 作用域链
    • 保证变量访问权限
    • 闭包保留外部作用域引用
  4. this 绑定
    • 全局:全局对象
    • 函数:取决于调用方式
  5. 栈大小限制:递归过深导致栈溢出

常见误区#

  1. 混淆作用域链与原型链:作用域链处理变量访问,原型链处理属性查找
  2. 误解 this:认为 this 指向函数自身或 EC 对象(实际由调用方式决定)
  3. 闭包内存泄漏:未释放外部变量的闭包(如事件回调)
  4. 变量提升误判:忽略 EC 创建阶段的变量/函数声明提升
  5. 递归爆栈:未优化尾递归导致栈溢出

应用场景#

场景执行上下文作用栈行为
全局代码执行创建全局 VO(windowGEC 始终在栈底
函数调用创建函数 AO(含参数/局部变量)FEC 入栈 → 执行 → 出栈
闭包保留外部函数 AO 的引用外部 FEC 出栈后 AO 仍被闭包引用
递归函数每次递归创建新 FEC递归过深导致栈溢出
构造函数调用创建新对象并绑定 this新 FEC 入栈,this 指向实例
事件处理创建新的 FEC(this 绑定事件目标)回调入栈执行

关联知识#

  1. 变量提升(Hoisting):EC 创建阶段的变量/函数声明初始化
  2. 闭包(Closures):函数保留其声明时作用域链的引用
  3. this 绑定规则
    • 默认绑定:window(非严格模式)
    • 隐式绑定:对象方法调用
    • 显式绑定:call/apply/bind
    • new 绑定:构造函数
  4. 作用域(Scope):词法作用域(静态) vs 动态作用域
  5. 内存管理
    • 闭包导致的外部变量内存不释放
    • 栈内存自动回收(EC 出栈时)
    • 堆内存通过 GC 回收
  6. 事件循环(Event Loop)
    • 执行栈清空后处理微任务/宏任务
    • 任务队列触发新 EC 创建

💡 黄金法则

  1. 每次函数调用 → 新建 EC → 入栈
  2. 函数执行完毕 → EC 出栈 → 销毁
  3. 栈底永远是全局 EC
  4. 闭包 = 函数 + 声明时的作用域链引用 使用 Chrome DevTools 的 Call Stack 面板直观查看执行栈!
JavaScript 执行上下文 & 执行上下文栈
https://website-truelovings-projects.vercel.app/posts/frontend/javascript/javascript-执行上下文--执行上下文栈/
作者
欢迎来到StarSky的网站!
发布于
2024-09-18
许可协议
CC BY-NC-SA 4.0