资讯动态

ECMA-262第六版中文翻译:JavaScript工程师的规范契约与调试指南

发布时间:2026/10/20 15:12:05 来源:尧图企业网站定制
简介本资源是ECMAScript 2015ES6语言规范的完整中文翻译项目成果面向前端开发者、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: EvaluationLexicalBinding : const BindingIdentifier InitializerLet 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是存储绑定的实际数据结构如DeclarativeEnvironmentRecordLexical 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已做全文搜索优化CtrlF 输入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, thena. 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, thena. If the [[ConstructorKind]] internal slot of F is derived, theni. 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]]直接创建新对象并绑定thisclass构造器[[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 立即 resolveasync () { await Promise.resolve(); return 1; }则至少经历一次 microtask。用queueMicrotask(() console.log(after))可验证。5. 进阶技巧用规范反向生成 TypeScript 类型定义5.1 为什么Promise.all的返回类型是PromiseT[]而非PromiseArrayTTypeScript 的lib.es2015.promise.d.ts中allT(values: readonly (T | PromiseLikeT)[]): PromiseT[];这个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 将ListT映射为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, thena. 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不能是 number1会被自动转为字符串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)按CtrlShiftP→Markdown Preview Enhanced: Open Preview to the Side点击链接即跳转。注意HTML 版本的锚点 ID 严格按规范小节编号生成如#sec-promise.all无需记忆直接复制 PDF 中的章节号即可。从那以后我每次遇到“这个行为到底合不合法”的疑问都强制走一遍“现象→关键词→HTML 锚点→算法步骤→抽象操作”的闭环。哪怕花 15 分钟也比在 Stack Overflow 上猜 2 小时靠谱。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑