Skip to content

第4章:执行上下文 —— 逻辑与物理的桥梁

“执行上下文不是‘一个东西’,而是‘栈帧 + Context + 指针’的协作系统。”
—— 当你看到“逻辑抽象”与“物理实体”的映射,你就掌握了 JS 执行的“操作系统”。

执行上下文(逻辑层)—— 规范中的“蓝图”

ECMAScript 规范 中,执行上下文被定义为一个抽象结构:

text
ExecutionContext = {
  ThisBinding: <this value>,
  LexicalEnvironment: <a Lexical Environment>,
  VariableEnvironment: <a Lexical Environment>
}

这是一个逻辑模型,用于描述:

  • 函数执行时的 this
  • 变量和函数的解析环境
  • 作用域链的起点

但它不是 JavaScript 代码能直接访问的对象,也不是内存中的一个“大盒子”。

物理实现映射 —— V8 引擎的“施工图”

当 V8 引擎真正执行代码时,它必须用 C++ 数据结构来“建造”这个逻辑模型。

以下是逻辑概念到物理实现的精确映射

逻辑概念物理实现存储位置
ThisBinding栈帧中的 receiver 字段栈内存(Stack)
LexicalEnvironment栈帧中的指针,指向 Context 对象栈内存(存指针)
VariableEnvironment栈帧中的另一个指针,指向 Context 对象栈内存(存指针)
变量绑定(如 x = 10存储在 Context 对象的 variables_ 数组中堆内存(Heap)
作用域链[[Environment]]previous_ 指针连接的 Context 链表堆内存(指针链)

真实案例:函数执行时的完整物理图景

js
function greet(name) {
  let message = "Hello, " + name;
  console.log(message);
}

greet("Alice");

greet 执行时,V8 构建了这样的物理结构:

1. 调用栈上创建栈帧

[调用栈]
┌─────────────────────────────┐ ← 栈顶
│ [greet 的栈帧]                │
│ function: greet              │
│ receiver: window             │ ← ThisBinding
│ argv: ["Alice"]              │
│ return_addr: 全局下一行      │
│ lex_env_ptr: → Context_greet │ ← LexicalEnvironment
│ var_env_ptr: → Context_greet │ ← VariableEnvironment
└─────────────────────────────┘

2. 堆上创建 Context 对象

[堆内存]
Context_greet:
  variables_[0]: name = "Alice"
  variables_[1]: message = "Hello, Alice"
  previous_: → 全局 Context

3. 函数对象的 [[Environment]] 指向当前 Context

text
greet.[[Environment]] → Context_greet

(用于支持可能的闭包)

关键结论:执行上下文是“协作系统”

执行上下文不是一个独立的实体,而是多个物理组件的协同工作:

1. 栈帧(Stack Frame) —— 提供“控制流”和 this

  • receiver 字段承载 ThisBinding
  • lex_env_ptrvar_env_ptr 指向 Context
  • return_addr 管理函数返回

2. Context 对象(堆上) —— 承载“变量状态”

  • variables_ 数组存储 let/const/var 变量
  • previous_ 指针连接外层作用域
  • 是变量的“户籍所在地”

3. 指针链([[Environment]]previous_ —— 构成“作用域链”

  • 决定变量查找路径
  • 支持闭包的“长期持有”能力

为什么理解这个映射至关重要?

1. 你不再“背概念”,而是“看本质”

  • “执行上下文是一个包含 this 和环境的对象”
  • “执行上下文是栈帧 + Context + 指针的协作系统”

后者能帮你诊断内存泄漏、优化性能、理解闭包。

2. 你理解了 let vs var 的底层差异

声明方式Context 中的处理
let / constLexicalEnvironment 指向的 Context 中创建
varVariableEnvironment 指向的 Context 中创建(通常是函数级)

这就是为什么 var 有变量提升,而 let 有暂时性死区(TDZ)。

3. 你明白了“为什么闭包慢”

  • 闭包需要创建 Context 对象(堆分配,比栈慢)
  • 变量查找需要遍历 [[Environment]] 链(比局部变量慢)
  • GC 需要管理 Context 的生命周期(增加 GC 压力)