简介:ECMAScript 6(ES2015)语言规范的中文翻译版本面向希望精读规范原文却受英文门槛限制的前端开发者、JavaScript 学习者及技术文档翻译者。该翻译并非简单摘要,而是对《ECMA-262 6th Edition》逐章进行的简体中文译稿,保留了从 Scope、Conformance、Normative references 到正式语法、词法语法、类型、执行上下文、函数定义等完整章节结构,便于对照英文原版进行深读。压缩包共 18 个文件、约 6.66MB,以 HTML 分章节页面为主,配合 PDF 总览、Markdown 说明、CSS 样式及 SVG/PNG 图表,既适合在线阅读,也方便离线检索或按部就班逐章研读。目前已有 261 人浏览学习。借助这套资料,读者可以系统掌握 let/const、箭头函数、解构赋值、Class、模块等 ES6 核心特性,也可作为日常开发时的权威扩展参考;项目以“静静地翻译,只和兴趣和明天有关”为出发点,内容完整且叙述平实,适合有一定基础、想深入语言规格的中高级学习者持续查阅。
1. 为什么需要一个靠谱的ECMAScript 6中文规范
先交代一下背景。几年前我刚转前端的时候,正好赶上ES6大规模普及的节点。日常开发里箭头函数、解构赋值、async/await这些特性用得很欢,但真到面试或者排查一个诡异的bug时,问题就来了:网上教程讲的都是“怎么用”,没人能清楚地告诉我“为什么是这个行为”。比如解构赋值的默认值到底在什么时机生效?箭头函数和普通函数的this差异,除了“继承外层this”这种口语化解释之外,规范层面是怎么定义的?我当时的做法是去翻英文原版ECMAScript 2015规范,也就是ECMA-262第6版,可翻了没几页就败下阵来,不是因为英文水平差多少,而是“规范语言”跟普通技术文章完全是两套表达体系,一句话里塞了三个引用、两个抽象操作和一个内部槽,没点耐心根本啃不动。
也是在那段时间,我关注到了es6规范中文翻译这个方向。ecma262-6-cn这个名字,简单直白,就是要把ECMAScript 6规范整体翻译成中文,让母语是中文的开发者不必先过英文阅读关,就能直接接触最权威的语言定义。它解决的痛点是实实在在的:社区里从来不缺ES6教程,缺的是把“规范级表述”翻成“人话级中文”的对照文本。教程是别人咀嚼过的二手知识,规范是一手事实,两者差距在遇到边界情况时尤其明显。
这个翻译项目适合谁用?我的判断是三类人。第一类是刚学完ES6语法、想进一步理解语言底层机制的进阶开发者;第二类是准备面试、想弄懂“规范如何规定闭包/作用域/原型链”的求职者;第三类是自己写工具库、要严格按规范行为做兼容处理的库作者。这三类人需求不同,但都需要一份结构完整、术语统一、能对着英文原版查证的中文规范。而且坦白讲,就算你已经有了不错的英文功底,一份高质量中文译本也能大幅压缩前期理解成本,把精力省下来去消化真正难懂的执行语义,而不是耗在逐句抠单词上。
还有一个很多人忽略的价值点:ES6是JavaScript语言演进的分水岭。从ES6开始,语言规范从每年一版变成了一年一版,后面ES7到ES13(也就是ES2016到ES2022)的很多机制,都是在ES6搭好的词法环境、作业队列、代理等骨架之上做增量。把ES6规范读透一遍,后面再看新版本提案,几乎可以平趟。这也是为什么就算过了这么多年,那份中文翻译仍然值得反复拿出来研究。
2. 规范正文的结构地图:先知道去哪查,再谈读懂
很多人第一次打开ECMA-262第6版时,会被它的体量吓到,正文加上附录接近600页。我最初也不知道该从哪读起,后来啃了几轮才摸清它的组织逻辑。ECMAScript规范不是一本从头读到尾的教程,它更像一部法律条文,前面章节定义概念,后面章节引用前面概念。正确的打开方式应该是“按图索骥”,先有一张地图,按需定位。
ECMA-262第6版的主体大致可以分成四块。第一块是最基础的词法与语法部分,涵盖字符编码、词法文法、表达式文法、语句定义,对应规范的第5章到第13章左右。第二块是执行语义相关的内容,包括作用域与词法环境、执行上下文、函数调用过程、Promise的作业队列机制,这部分散布在第8、9、10章。第三块是所有内置对象的定义,从Object、Function、Array到RegExp、Date、JSON,每一个内置对象都有独立的章节,描述它的构造器行为、原型方法、内部插槽。第四块是语法/语义的补充控制,比如严格模式条款、遍历器与生成器、模块机制等,散落在各章之间。
这里我整理一个快速定位的对照表,方便你们按需求直接翻到对应章节:
| 想查什么 | 对应章节区域 | 重点看什么 |
|---|---|---|
| 字符编码与标识符合法性 | 第5章、第6章 | 源码字符集、Unicode转义、关键字表 |
| let/const与块级作用域 | 第8章、第13章 | 词法环境、声明实例化时机 |
| 箭头函数/普通函数的this绑定 | 第9章、第10章 | [[ThisMode]]、函数调用流程 |
| 原型链与继承 | 第8章、第19章 | 内部槽[[Prototype]]、getPrototypeOf流程 |
| Promise与微任务 | 第8章、第25章 | 作业队列、PromiseReactionJob |
| class语法与super | 第12章、第14章 | 类定义语义、方法解析过程 |
| 模块import/export | 第15章 | ModuleRecord、实例化与求值 |
我一直觉得,读规范最忌“线性阅读”。你从头开始背词法定义,大概率在第五章就被SourceCharacter和Unicode相关的内容拖垮。更实际的做法是:先看目录,圈出跟你当前问题强相关的章节,只读那几节,然后再沿着引用链向外部扩散。比如当初我搞不懂“为什么let声明的变量在不同代码块里互不干扰”,定位到第8章的词法环境(Lexical Environment),顺着它又读了第13章的块级语句运行时语义,很快就理解了“每个块记录一个EnvironmentRecord”这个核心设计。
还有一点需要适应的是规范的“前置引用”习惯。它经常在没解释某个概念时就先用上,后面才正式定义。比如第8章就拿“执行上下文栈”来解释函数调用,但执行上下文的完整定义可能要到第9章。遇到这种情况千万别死磕,打个标记继续往下走,读到后面再回来看,通常会有豁然开朗的感觉。
3. 翻译“规范语言”的难点:术语、模态词和算法伪代码
真正参与过这类翻译项目的人都知道,规范翻译最难的从来不是拷贝粘贴,而是把一套高度形式化的英文法律文本,转写成中文后仍然保持“精确性”。ECMAScript规范的英文写得很“拧巴”,每个词都是有意选择的,翻译时稍不留神就会改变语义,这是翻译工作最核心的挑战。
首先是术语统一问题。同一个英文术语,在国内社区可能有好几种译法,比如“lexical environment”,有人译“词法环境”,有人译“词法作用域”;“binding”有人译“绑定”,有人译“约束”。翻译项目必须在一开始就建立一份术语表,锁定每一个核心概念的译法,并且全文强制统一。比如“identifier”统一译“标识符”,“reference”统一译“引用”,“internal slot”统一译“内部槽”,“ordinary”尽量译“常规”,避免与“标准/普通”混用。术语表不是拍脑袋定的,得考虑中文技术圈已有的惯用译法,同时兼顾原词在规范中的特定含义。像“assignment”在普通语境下能译“赋值”,但在“DestructuringAssignmentPattern”里,把它译成“解构赋值模式”就比“解构分配模式”顺耳得多。
其次是模态词的精确翻译。英文规范里“must”“should”“may”不仅仅是语气词,它们对应着不同的规范约束等级。must是强制性要求,should是推荐行为,may是允许实现自行选择。中文翻译如果不加区分全译成“可以”或“应该”,语义就乱了。我们的做法是:must一律译“必须”,should译“应当”,may视语境译成“可以”或“允许”,并且在项目内约定这些词的强制等级含义,方便读者快速识别条款的约束强度。这个细节看着简单,但恰恰是区分“翻译规范”和“翻译普通文档”的分水岭。
最难啃的还是算法伪代码。ECMAScript规范描述运行时行为时常用伪代码形式呈现,比如定义某个抽象操作时有If、Return、Throw这样的步骤序列。翻译伪代码不能照字面直译,得保留原有步骤编号和分支结构,同时把注释和步骤表述转换成中文。举例来说,规范里频繁出现“Let T be a new ordinary object with no own properties”,直译是“令T为一个新的且没有自有属性的常规对象”,听起来僵硬,但放在伪代码语境里反而需要这种僵硬,因为严谨性优先于流畅性。我们一般保持“令”“返回”“抛出”这类动词的固定用法,让每个算法翻译出来都像一段可读的中文伪代码,长句则拆成短句并用分号衔接。
翻译过程中还有一个绕不开的麻烦:规范里大量使用内部记法,比如[[Call]]、[[GetOwnProperty]]、[[Enumerable]]这样的双中括号写法,以及@%FunctionPrototype%这种宿主对象标记。这些符号我建议一律保留原文不译,因为它们在代码和规范里成对出现,翻译后反而会破坏可追踪性。第一次看的人可能会疑惑“中括号里面是什么”,其实它表示的是对象内部不可直接访问的插槽或内部方法,是规范为了精确描述行为而设的抽象层。保留原文符号,然后在术语表里加一条说明,比强行塞一个中文译名要稳得多。
4. 从规范视角看ES6核心特性:几个值得精读的章节实例
光讲翻译方法有点空,我举几个具体例子,说说拿中文规范精读ES6特性之后,到底能收获什么。这三个例子,也是我推荐给身边团队新人的“必读篇目”。
第一个是let/const与块级作用域。教程里通常就一句话:“let声明的变量只在当前块内有效”。可规范里的表述要复杂得多。第13章的块级语句运行时语义规定,每进入一个块,运行时都会创建一个新的词法环境(Lexical Environment),该环境的词法环境记录器(Environment Record)负责登记块内让let和const声明的绑定。这就是为什么循环体里用let声明迭代变量能形成“每次迭代一个新绑定”的效果,因为每次循环迭代都会创建新的词法环境。中文规范把这段语义梳理清楚后,你不仅知道了结果,还知道了结果是如何被一步步推导出来的。
第二个是箭头函数与词法this。这可能是中文技术圈误解最多的一个点。很多人以为箭头函数“没有自己的this”,但规范层面更准确的说法是:箭头函数实例有一个内部槽叫[[ThisMode]],它的值是lexical,普通函数则是global或strict。当函数被调用时,运行时先检查[[ThisMode]],如果是lexical,不执行“绑定this”的步骤,而是直接把当前执行上下文里的this值作为函数体内的this解析结果。注意,这一步是在规范第10.4.3节“引用规范类型的函数调用”里明确定义的,普通函数和箭头函数的差异,本质上不是“this丢了”,而是调用约定不同。我见过不少开发者在这上面栽跟头,读一遍中文规范对应章节后基本不会搞混。
第三个是Promise的作业队列机制。第8.4节定义了Job和Job Queue,第25章则是Promise的内置对象语义。规范明确说明,Promise的then回调不是直接异步执行,而是被包装成PromiseReactionJob放进作业队列,当前执行上下文栈清空后、宿主环境才会执行队列里的作业。这能解释很多奇怪现象,比如为什么Promise回调与setTimeout的执行顺序在不同的宿主环境里可能不一致,因为宿主可以有自己的作业队列调度策略。中文规范把这些机制串起来后,对事件循环和微任务的理解就从“背面试题”升级为“看实现”。
这三个例子共同说明一件事:ES6规范不只是在定义新语法,它在重新定义JavaScript的执行模型。精读中文规范,收获的也不是个别语法点,而是一整套“如何阅读语言定义”的思维框架。
5. 中文翻译版的正确用法:对照阅读、溯源、参与共建
中文规范的价值我肯定,但我还是要泼一盆冷水:不要把它当成唯一权威。规范译本再好,本质仍是二手文本,任何翻译都带有译者的理解和措辞偏向,所以读的时候必须掌握几个基本姿势,才能发挥它最大的作用。
姿势一:遇到歧义必须回查原文。哪怕译文质量再高,个别句子也可能出现中文读起来模棱两可的情况。我打个比方,一名译者在翻译“The production is left-recursive”时,没有把left-recursive翻译得很贴切,导致读起来像是“这个产生式是递归的”。这就产生了歧义。新读者如果只盯着中文看,永远发现不了问题。所以我的习惯是,阅读时重点关注那些带“必须”和“如果”的条款句,一旦觉得逻辑别扭,立刻打开英文原版对应段落对照。中文翻译版是地图,英文原版是地面,地图只能帮你指路,踩到坑还是得看地面。
姿势二:直接利用术语表建立自己的“翻译词典”。我在参与这类项目时受益最大的习惯,是专门维护一个个人笔记,把中文译名、英文原词、以及自己对这个概念的一句通俗解释列成表。比如“可选链(optional chaining)”的特性还没普及前,我就一边啃规范一边记:“PropertyAccessor”和“OptionalExpression”在语法树里的区别是什么。这本个人词典不仅能帮我考试面试,后来写作、给团队做技术分享时,也是现成的素材库。读中文规范的时候,建议你也同步建一份这样的词典,效果比单纯阅读要好得多。
姿势三:积极参与翻译细化和维护的过程。纯开源翻译项目通常没有强约束,遇到想修正的术语勘误、想补充的注释,最好的方式就是提Issue或Pull Request。我记得有一版术语表里“module”被统一译成“模块”,但有人指出规范第15章里“Module”特指模块记录对象,跟日常说的“模块文件”不是同一个概念,于是大家讨论后约定:在描述数据结构的语境下保留英文“Module”,在讲语法特性的语境下才译“模块”。这种细节如果没有社区反馈,光靠一两个人是很容易翻车的。如果你发现中文译文里有读不通的地方,先别急着怀疑自己理解有问题,去提一个勘误建议,这既是在帮项目,也是在逼你精读原文。
最后再说个使用建议。读ES6规范,尤其是中文译本,我建议你给自己定个任务式目标,比如“这周我要彻底搞懂class里的super到底做了什么”,只围绕一个主题把相关章节啃下来。别指望一次性通读整本,那是学术研究者才需要做的事。对我们写业务代码的人来说,带着问题查规范,查完马上动手写验证demo,这才是效率最高也最不容易忘的思路。当初我啃Promise和作业队列那部分,就是边写边用一个递归Promise的小例子反复验证,才算真正把这些抽象概念落到实处的。
本文还有配套的精品资源,点击获取