ECMAScript 规范简介
ECMAScript 规范(ECMA-262)不是一本“教程”,而是一份语言实现的工程蓝图。它定义了 JavaScript 引擎(如 V8、SpiderMonkey)必须如何工作。
你已经发现了 [[Environment]]、[[Prototype]]、[[ThisBinding]]、[[LexicalEnvironment]] 这些“内部槽”——这说明你已经从“使用者”进入了“观察者”阶段。
现在,我为你系统性地汇总那些真正影响你深度理解 JavaScript 的规范级概念,并解释它们如何串联起你已知的知识。
一、核心:执行模型(Execution Model)
这是你理解“代码如何运行”的基础。
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| Execution Context(执行上下文) | §8.3 | 每次函数调用或脚本执行时创建的“运行时环境” | 是理解 this、作用域、调用栈的根基 |
| Call Stack(调用栈) | 隐含在执行上下文管理中 | 后进先出(LIFO)的栈,管理上下文的执行顺序 | 解释递归、爆栈、异步回调的执行顺序 |
| Realm | §8.2 | 一组全局对象、内置对象(如 Array、Date)的集合 | 解释 iframe 间 instanceof 失效的原因 |
| Job Queue & Event Loop | §8.6 | 异步任务(Promise、setTimeout)的队列 | 理解宏任务/微任务、事件循环的核心 |
你已知的“this”、“作用域”、“闭包”都依赖于 Execution Context 和 Realm。
二、作用域与闭包的底层机制
你已经理解了 [[Environment]],但规范中还有更细的结构。
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| Lexical Environment(词法环境) | §6.2.3 | 包含 环境记录(Environment Record) 和 外部引用(outer) | 是“作用域链”的真实实现 |
| Environment Record(环境记录) | §6.2.4 | 存储变量绑定的实际结构(如对象环境记录、声明式环境记录) | 解释 var 和 let 的不同行为 |
| Declarative Environment Record | §6.2.4.1 | 用于 let、const、function(块级) | 支持块级作用域 |
| Object Environment Record | §6.2.4.2 | 用于 with、全局环境 | 变量绑定到对象属性 |
| Global Environment | §8.2.3 | 全局作用域的特殊环境,包含 var、函数、let 的分离管理 | 解释为什么全局 let 不挂在 window 上 |
闭包的本质:函数创建时,其 [[Environment]] 指向当前 Lexical Environment,形成“捕获”。
即使外层函数执行完毕,只要闭包存在,Environment Record 就不会被 GC。
三、对象与原型系统的内部机制
你已经知道 [[Prototype]] 是内部槽,但还有更多。
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| [[Prototype]] | §6.1.7.2 | 对象的原型链链接 | __proto__、Object.create、class 的基础 |
| [[Prototype]]` 的隐式性 | [[Get]]、[[Set]] 等内部方法会自动沿原型链查找 | 属性访问的“自动向上查找”机制 | 理解“继承”的本质 |
| Internal Methods(内部方法) | §7.3 | 如 [[Get]]、[[Set]]、[[HasProperty]] 等 | 代理(Proxy)拦截的就是这些方法 |
| Ordinary vs Exotic Objects | §7.2 | 普通对象 vs 特殊对象(如数组、arguments) | 解释为什么 Array 的 length 能自动更新 |
Proxy 的本质:拦截对象的内部方法([[Get]]、[[Set]] 等),实现自定义行为。
四、函数与 this 的绑定机制
你已经知道 [[ThisBinding]],但函数的创建和调用更复杂。
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| [[ThisMode]] | §9.4.6 | 函数的 this 模式:lexically(箭头函数)或 global/strict | 解释箭头函数没有自己的 this |
| Function Environment Record | §9.4.6 | 函数执行上下文的环境记录,包含 [[ThisBinding]] 和 [[ThisMode]] | this 绑定的实现位置 |
| [[Construct]] vs [[Call]] | §7.3.13 | 构造调用(new)和普通调用的区别 | new 做了什么? |
| HomeObject | §10.2.3 | super 查找的起点 | super.method() 是怎么找到父类方法的? |
super 的实现:依赖 HomeObject 和 [[Prototype]] 链。
五、类型系统与抽象操作
这是 JavaScript “奇怪行为” 的根源。
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| ToPrimitive | §7.1.1 | 对象转原始值(toString/valueOf) | 解释 [0] == false、{} + [] |
| ToString / ToNumber | §7.1.12 / §7.1.5 | 类型转换规则 | +"1"、String(obj) 的底层 |
| Abstract Operations | 第 7 章 | 如 IsCallable、GetV、InstanceofOperator | 所有操作符、方法调用的底层逻辑 |
| SameValue / SameValueZero | §7.2.12 / §7.2.13 | Object.is() 和 === 的区别 | 为什么 Object.is(-0, +0) 是 false |
“==” 的诡异行为:源于 ToPrimitive 和抽象相等比较算法(Abstract Equality Comparison)。
六、模块与作用域的现代机制
| 概念 | 位置 | 作用 | 为什么重要 |
|---|---|---|---|
| Module Environment Record | §9.4.8 | 模块作用域的环境记录 | import 是如何实现“只读绑定”的? |
| Import/Export Binding | §9.4.8 | 模块间的动态绑定 | import 不是“拷贝”,而是“链接” |
| Temporal Dead Zone (TDZ) | §5.4.2 | let/const 在声明前访问报错 | 理解“暂时性死区” |
ES 模块是“活绑定”:import 的值会随 export 变化而变化。
七、你可能还没意识到,但极其重要的概念
| 概念 | 为什么重要 |
|---|---|
| Agent | 类似“线程”的概念,一个 Agent 有自己的 Realm、堆栈、堆。多 iframe 或 Web Worker 就是多个 Agent。 |
| Job vs Job Queue | Promise 的 then 回调是 微任务(Microtask),属于 PromiseJobs 队列。setTimeout 是宏任务。 |
| Completion Records | 每个语句执行后返回一个 { [[Type]]: return/break/continue, [[Value]], [[Target]] },解释 try/catch/finally 的控制流。 |
八、如何阅读规范?
- 不要从头读,按需查阅。
- 用英文读,中文翻译常有误差。
- 结合 V8 博客:v8.dev 会解释规范如何实现。
- 用《You Don't Know JS Yet》作为导读:它用通俗语言解释规范思想。
总结:你已掌握的“认知钥匙”
你已经拿到了以下“钥匙”:
| 你已知 | 规范术语 | 作用 |
|---|---|---|
| “隐式指针” | [[Environment]]、[[Prototype]] | 作用域链、原型链的链接机制 |
| “执行上下文” | ExecutionContext | 包含 [[LexicalEnvironment]] 和 [[ThisBinding]] |
| “闭包” | 函数 [[Environment]] 捕获外层环境 | 变量生命周期延长 |
| “this” | [[ThisBinding]] | 执行上下文的状态字段 |