简介:本资源是ECMAScript 2015(ES6)语言规范的完整中文翻译项目成果,面向前端开发者、JavaScript语言研究者及标准学习者,解决英文原版阅读门槛高、术语理解难、规范细节不易掌握等实际问题。压缩包共18个文件,含4个HTML章节页(用于结构化浏览规范正文)、2个PDF(含主规范文档Ecma-262-6.pdf及说明性材料)、4个PNG与2个SVG图示文件(直观呈现语法结构、执行模型等关键图表)、2个CSS与ICO/JPG资源(保障网页端阅读体验),整体体积6.66MB,轻量易下载。目前已有262人学习下载,体现社区对标准化文档本地化工作的持续关注。读者可直接获取可离线查阅的中文规范全文、配套图解与目录导航体系,尤其适合深入理解let/const、模块、Promise、Proxy等ES6核心机制的设计原理与语义定义,是系统研读语言标准不可多得的参考材料。
1. 这不是“语法糖合集”:ECMA-262 第六版中文翻译是 JS 工程师绕不开的底层契约书
很多人第一次点开ecma262-6-cn仓库时,下意识以为这是个“ES6 新特性速查表”——结果发现满屏是LetDeclaration,AssignmentExpression,Runtime Semantics: Evaluation这类术语,瞬间关掉。但恰恰是这份被多数人跳过的中文翻译,成了我在某跨平台系统重构中定位let块级作用域与var变量提升混用导致内存泄漏的唯一依据。它不教你怎么写箭头函数,而是定义“箭头函数在词法环境中的绑定行为必须满足 Runtime Semantics: ArrowFunctionEvaluation 的第 4 步”。如果你常遇到“这段代码在 Chrome 跑得通、Node.js 报错、TypeScript 类型推导崩了”的玄学问题,根源往往不在运行时差异,而在你和引擎对同一段规范文本的理解偏差。这份翻译覆盖全部 26 章、300+ 小节,从词法文法(Lexical Grammar)到抽象操作(Abstract Operations),从 Promise 的PerformPromiseThen到 Proxy 的[[Get]]内部方法,是 JS 引擎实现者、Babel 插件开发者、以及需要写出零歧义代码的资深前端工程师的底层契约书。它不适合当入门教程,但当你需要确认“for...of循环中break是否会触发IteratorClose”,它就是你的后悔药。
2. 为什么必须用第六版?从const的不可变性陷阱说起
2.1 第六版的核心分水岭:从“语法扩展”到“运行时语义建模”
ES5 规范像一本操作手册:告诉你Object.create()怎么用、JSON.parse()接受什么参数。而 ES6(即 ECMA-262 第六版)首次系统性引入抽象操作(Abstract Operations)和算法步骤(Algorithm Steps)来精确描述行为。以const为例,ES5 只说“声明常量”,但没定义“重新赋值时引擎该抛什么错误、错误对象该有哪些属性、是否影响后续执行”。第六版则在第 13.6.4 节明确定义:
Runtime Semantics: Evaluation
LexicalBinding : const BindingIdentifier Initializer
- Let bindingId be StringValue of BindingIdentifier.
- Let lhs be ResolveBinding(bindingId, strict: true).
- ReturnIfAbrupt(lhs).
- Let rhs be the result of evaluating Initializer.
- ReturnIfAbrupt(rhs).
- Perform ? PutValue(lhs, rhs).
关键在第 6 步的PutValue—— 它指向第 6.2.3.2 节,其中明确要求:若目标引用(Reference)的strict标志为 true 且IsStrictReference(ref)返回 true,则必须抛出TypeError。这意味着const a = {}; a.b = 1;合法(修改对象属性),但a = {};非法(重绑定引用),而这个边界完全由PutValue的算法路径决定。没有第六版,你只能靠试错;有了它,你能直接定位到算法分支,预判所有边缘 case。
2.2 中文翻译的不可替代性:术语一致性决定调试效率
英文原版中binding,reference,environment record,lexical environment等概念高频穿插,且存在细微差别。例如:
Binding指“标识符与值的关联关系”(如let x = 1中x与1的绑定);Reference是一个三元组{ base, referencedName, strict },用于PutValue/GetValue操作;Environment Record是存储绑定的实际数据结构(如DeclarativeEnvironmentRecord);Lexical Environment是包含Environment Record和外层引用的容器。
中文翻译将这些统一译为“绑定”“引用记录”“环境记录”“词法环境”,并在附录 A 的术语表中明确定义。我曾因混淆binding和reference,误以为const禁止所有修改,实际它只禁止对Reference的PutValue操作。而中文版第 8.7 节“引用类型”表格清晰列出Reference的三个字段及其含义,比反复查英文 glossary 高效得多。
2.3 如何快速定位你要的章节?按“问题现象”反向查规范
别从目录开始读。工程实践中,我习惯用三步定位法:
- 现象归类:遇到
await在非 async 函数中报错?归为“语法错误”;Promise.allSettled返回值结构异常?归为“抽象操作返回值”; - 关键词锚定:在 PDF 全局搜索
await→ 跳转到第 14.6.1 节 “Async Function Definitions”; - 算法路径追踪:找到
AwaitExpression : await Expression的 Runtime Semantics,再顺藤摸瓜到Await抽象操作(第 7.2.3.1 节),最终确认其要求Expression必须返回Thenable对象,否则抛TypeError。
提示:GitHub 仓库的
index.html已做全文搜索优化,Ctrl+F 输入IteratorClose或PerformPromiseThen可秒级定位。PDF 版本建议用 Adobe Acrobat 的“高级搜索”,勾选“匹配整个单词”。
3. 实战:用规范验证class继承链的初始化顺序
3.1 问题场景:为什么子类构造函数里super()必须最先调用?
某次重构中,A同学把super()放在this.xxx = yyy之后,Chrome 报ReferenceError: Must call super constructor in derived class before accessing 'this'。他疑惑:“V8 为什么这么严格?Babel 编译后能跑啊。” —— 这正是规范与实现的分界点。我们直接查第六版第 14.5.14 节 “Runtime Semantics: ClassDefinitionEvaluation”。
关键算法步骤:
- If ClassHeritage is not present, then
a. Let protoParent be the value of the [[Prototype]] internal slot of the current Realm’s global object.- Else,
a. Let heritageResult be the result of evaluating ClassHeritage.
b. ReturnIfAbrupt(heritageResult).
c. Let protoParent be ? Get(protoParent, "prototype").
...- Let constructorInfo be DefineMethod of Constructor with arguments protoParent, constructorParent, and false.
- Let F be constructorInfo.[[Closure]].
- Set the [[ConstructorKind]] internal slot of F to "derived".
- Return F.
重点在第 21 步:[[ConstructorKind]]被设为"derived"。这触发了第 14.2.14 节 “Runtime Semantics: EvaluateBody” 中对this的检查逻辑:
- If thisArg is not undefined, then
a. If the [[ConstructorKind]] internal slot of F is "derived", then
i. If the [[ThisBindingStatus]] internal slot of F is not "initialized", throw a ReferenceError exception.
而[[ThisBindingStatus]]仅在super()执行完毕后由Construct抽象操作设为"initialized"(见第 7.3.13 节)。因此,任何在super()前访问this的行为,都会在第 22.a.i 步被规范强制拦截。Babel 编译能跑,是因为它用_this = _possibleConstructorReturn(this, _getPrototypeOf(...))模拟了this初始化,但这是对规范的“兼容性补丁”,而非规范本身允许的行为。
3.2 验证工具:用esprima解析 AST 对照规范文法
光看文字不够直观?我们用代码验证class声明的词法结构是否符合规范。第六版第 14.5 节定义了ClassTail文法:
ClassTail : ClassHeritageopt { ClassBodyopt }用esprima解析class A extends B { constructor() { super(); } }:
const esprima = require('esprima'); const ast = esprima.parseScript('class A extends B { constructor() { super(); } }', { ecmaVersion: 6, tokens: true }); console.log(JSON.stringify(ast.body[0].type)); // "ClassDeclaration" console.log(ast.body[0].superClass.type); // "Identifier" (对应 ClassHeritage) console.log(ast.body[0].body.body[0].type); // "MethodDefinition" (constructor)对照规范第 14.5.1 节 “ClassDeclaration : class BindingIdentifier ClassTail”,superClass字段的存在,正是ClassHeritageopt文法项被匹配的证据。而body中的MethodDefinition,则对应ClassBody中的ClassElement(第 14.5.15 节)。这种 AST 层级的映射,让你能把代码结构直接锚定到规范条款,避免“我以为是这样,其实规范写的是那样”的认知偏差。
3.3 常见误区:class不是语法糖,而是全新语义实体
很多教程说“class是function的语法糖”,这是严重误导。第六版明确将class定义为独立的声明类型(Declaration),其[[Construct]]内部方法与普通函数完全不同:
- 普通函数:
[[Construct]]直接创建新对象并绑定this; class构造器:[[Construct]]必须先调用super()获取父类实例,再执行子类constructor体(第 9.2.2 节)。
验证方式:在控制台执行:
class A {} console.log(A.prototype.constructor === A); // true console.log(Object.getPrototypeOf(A) === Function.prototype); // true // 但 A 的 [[Construct]] 行为由 ClassDefinitionEvaluation 生成,而非 FunctionDeclaration规范第 14.5.14 节的算法明确区分了DefineMethod(用于class方法)和CreateDynamicFunction(用于function表达式),二者生成的闭包具有不同的内部槽位(Internal Slots)。这就是为什么class A {}不能被new A()之外的方式调用——它的[[Call]]内部方法被规范定义为ThrowTypeError(第 9.2.1 节)。
4. 避坑:中文翻译里的五个典型理解偏差与修正方案
4.1 现象:let声明的“暂时性死区(TDZ)”在某些浏览器中表现不一致
原因:中文翻译将Temporal Dead Zone译为“暂时性死区”,但未强调其本质是运行时语义约束,而非语法错误。规范第 8.1.1.4 节规定:在let绑定初始化完成前,任何对该绑定的GetValue操作都必须抛ReferenceError。但部分旧版 V8 在typeof x(未声明变量)时返回"undefined",而对 TDZ 中的x却抛错——这并非 bug,而是typeof操作符在第 12.5.8 节被特别豁免:If Type(val) is Reference and IsUnresolvableReference(val) is true, return "undefined"。但 TDZ 中的x是Reference,且IsUnresolvableReference为 false(因为已声明),故不触发豁免。
解决:永远不要依赖typeof检测 TDZ 变量;用try/catch或确保声明在访问前执行。
4.2 现象:Promise.race([])立即 resolve,但Promise.all([])也立即 resolve,为何行为相同?
原因:中文翻译第 25.4.4.3 节 “Promise.race ( iterable )” 的算法第 4 步写:“If iteratorRecord.[[Done]] is true, return promise.” 但未说明iteratorRecord.[[Done]]在空数组时如何为 true。需追溯到第 25.1.1.2 节 “CreateArrayIterator” —— 空数组的迭代器在next()调用时直接返回{value: undefined, done: true}。而Promise.race的算法第 3 步Let iteratorRecord be GetIterator(iterable)创建迭代器后,第 4 步即检查[[Done]]。同理,Promise.all的第 5 步也检查iteratorRecord.[[Done]]。
解决:记住“空可迭代对象的迭代器默认done: true”,这是规范对性能的优化约定,非特例。
4.3 现象:Object.is(-0, +0)返回false,但===返回true,中文翻译未解释底层原理
原因:中文翻译第 7.2.10 节 “Object.is ( value1, value2 )” 的算法第 1 步:“If SameValueNonNumber(value1, value2) is true, return true.” 而SameValueNonNumber在第 7.2.11 节定义为:If Type(x) is Number and Type(y) is Number, then return ! SameValueZero(x, y).关键在SameValueZero(第 7.2.12 节):它要求x和y的符号位(sign bit)必须相同,而-0和+0的 IEEE 754 符号位不同。
解决:Object.is是为Map键比较设计的,-0和+0作为不同键是合理需求;日常比较仍用===。
4.4 现象:for...in遍历顺序在中文翻译中未明确,导致跨浏览器不一致
原因:中文翻译第 13.7.5.15 节 “Runtime Semantics: ForIn/OfBodyEvaluation” 仅规定“枚举对象的可枚举自有属性”,但未定义顺序。规范第 9.1.12 节 “EnumerateObjectProperties” 注明:“The mechanics and order of enumerating the properties is not specified.” 这意味着for...in顺序由引擎实现决定,V8 用插入顺序,Chakra 用定义顺序。
解决:绝不在业务逻辑中依赖for...in顺序;需有序遍历时用Object.keys(obj).sort().forEach()。
4.5 现象:async函数返回的 Promise 状态变化时机难以预测
原因:中文翻译第 14.6.14 节 “Runtime Semantics: AsyncFunctionStart” 的算法第 3 步:“Let result be the result of evaluating AsyncFunctionBody.” 但AsyncFunctionBody的求值过程(第 14.6.15 节)规定:若 body 中无await,则result为Completion{[[Type]]: normal, [[Value]]: undefined, [[Target]]: empty},此时 Promise 立即 resolve;若有await,则进入Await抽象操作,状态变为 pending。
解决:async () => 1返回的 Promise 立即 resolve;async () => { await Promise.resolve(); return 1; }则至少经历一次 microtask。用queueMicrotask(() => console.log('after'))可验证。
5. 进阶技巧:用规范反向生成 TypeScript 类型定义
5.1 为什么Promise.all的返回类型是Promise<T[]>而非Promise<Array<T>>?
TypeScript 的lib.es2015.promise.d.ts中:
all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;这个T[]并非随意设计。我们查第六版第 25.4.4.1 节 “Promise.all ( iterable )” 的算法:
- Let values be a new empty List.
- Let nextIndex be 0.
- Repeat, while nextIndex < len,
a. Let nextValue be Get(iterable, ToString(nextIndex)).
b. Append nextValue to values.
c. Increment nextIndex by 1.- Let promiseResolve be Get(promise, "resolve").
- Let result be Call(promiseResolve, promise, «promise»).
- Let resultCapability be NewPromiseCapability(result).
- Let index be 0.
- Let valuesLen be the length of values.
- Let remainingElementsCount be valuesLen.
- Let resultValues be a new List of length valuesLen.
关键在第 5 步和第 14 步:values是一个List(规范中的抽象数据结构),而resultValues是同样长度的新List。规范虽未定义List的 TypeScript 类型,但第 7.1 节 “List and Record Specifications” 明确List是“有序元素集合”,且所有操作(Append,Get)均按索引访问。这与 JavaScript 数组的length和索引访问语义完全一致。因此,TS 将List<T>映射为T[]是对规范语义的精准还原,而非妥协。
5.2 从Proxy的[[Get]]内部方法推导gettrap 的参数签名
第六版第 9.5.2.1 节 “[[Get]] (P, Receiver)” 定义:
- Assert: Type(O) is Object.
- Assert: IsPropertyKey(P) is true.
- Let handler be O.[[ProxyHandler]].
- If handler is null, throw a TypeError exception.
- Let target be O.[[ProxyTarget]].
- Let trap be ? Get(handler, "get").
- If trap is undefined, then
a. Return ? Get(target, P, Receiver).- Let trapResult be ? Call(trap, handler, «target, P, Receiver»).
- Let booleanTrapResult be ToBoolean(trapResult).
- If booleanTrapResult is false, return undefined.
- Return trapResult.
参数«target, P, Receiver»直接对应 TS 的get(target: T, p: PropertyKey, receiver: any): any。注意P的类型是PropertyKey(第 6.1.5 节定义为String | Symbol),这解释了为何gettrap 中p不能是 number(1会被自动转为字符串"1")。而Receiver参数决定了this绑定,规范第 9.5.2.1 节注释:“The Receiver parameter is used to determine the correctthisvalue for thegetoperation.” —— 这正是Reflect.get(target, key, receiver)的设计依据。
5.3 构建自己的规范查询工作流:VS Code + Markdown Preview
我日常将ecma262-6-cn的 HTML 版本拖入 VS Code,配合以下配置实现秒级查询:
- 安装插件
Markdown Preview Enhanced; - 在
.vscode/settings.json中添加:
{ "markdown-preview-enhanced.enableScriptExecution": true, "markdown-preview-enhanced.previewTheme": "github-light.css", "editor.quickSuggestions": { "strings": true } }- 创建
ecma-search.md文件,粘贴常用搜索片段:
### 快速跳转 - [词法文法](#sec-lexical-grammar) - [Promise.all](#sec-promise.all) - [Proxy [[Get]]](#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver) - [await 表达式](#sec-async-function-definitions-runtime-semantics-evaluation)- 按
Ctrl+Shift+P→Markdown Preview Enhanced: Open Preview to the Side,点击链接即跳转。
注意:HTML 版本的锚点 ID 严格按规范小节编号生成(如
#sec-promise.all),无需记忆,直接复制 PDF 中的章节号即可。
从那以后我每次遇到“这个行为到底合不合法”的疑问,都强制走一遍“现象→关键词→HTML 锚点→算法步骤→抽象操作”的闭环。哪怕花 15 分钟,也比在 Stack Overflow 上猜 2 小时靠谱。希望帮到你。
本文还有配套的精品资源,点击获取