本文回答了关于下一版 JavaScript(ECMAScript 2016)的以下几个问题:
- 谁在设计?
- 如何设计?
- 会有什么特性?
本文会持续更新以及时反馈最新进展。
1、谁在设计 ECMAScript?
答:TC39 (Technical Committee 39)。
TC39 是推进 JavaScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。会议纪录都可在网上查看,可以让你对 TC39 如何工作有一个清晰的概念。
有时候(实际上本文就如此),你会发现 TC39 会员这个词被用来指代一个人,其实指的是 TC39 会员公司派出的代表。
很有意思的是,TC39 实行的是协商一致的原则:通过一项决议必须得到每一位会员(公司代表)的赞成。
2、如何设计 ECMAScript?
2.1 问题:ECMAScript 2015 (ES6) 这个版本太大了
最近发布的 ECMAScript(ES6)新增内容很多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度如此之大主要有两大原因:
- 比新版率先完成的特性,必须等待新版的完成才能发布。
- 那些需要花长时间完成的特性,也顶着很大的压力被纳入这一版本,因为如果推迟到下一版本发布意味着又要等很久,这种特性也会推迟新的发布版本。
因此,从 ECMAScript 2016(ES7)开始,版本发布将会变得更加频繁,每年发布一个新版本,这么一来新增内容也会更小。新版本将会包含每年截止时间之前完成的所有特性。
2.2 解决方案: TC39 设计过程
每个 ECMAScript 特性的建议将会从阶段 0 开始, 然后经过下列几个成熟阶段。其中从一个阶段到下一个阶段必须经过 TC39 的批准。
阶段 0: Strawman 初稿
什么是 Strawman?一个推进 ECMAScript 发展的自由形式的想法。该想法必须由 TC39 的会员提交,如果是非会员则必须注册成为 TC39 贡献者才能提交。
必备条件:文件必须在 TC39 的会议上通过审议(原文),然后才能加入阶段 0 的建议页面。
阶段 1:Proposal 建议
什么是 Proposal?一份新特性的正式建议文档。
必备条件:必须确定一位带头人来为负责这份建议。无论是带头人或者联合带头人都必须是 TC39 的会员(原文)。建议要解决的问题必须以简明的文字描述,而解决方案则要给出相应的实例和 API,并详细描述语义及算法。最后,必须指明此建议的潜在问题,例如与其他特性之间的关联,实现难点等。实现方式,polyfills 和 demo 也是需要的。
下一步:通过一个阶段 1 的建议,表明 TC39 愿意研究、讨论并促成该建议。接下来,我们就可以期待该建议的重大改变了。
阶段 2:Draft 草案
什么是 Draft?草案是规范的第一个版本。其与最终标准中包含的特性不会有太大差别。
必备条件:建议此时必须要附加该特性的语法和语义的正式说明(使用 ECMAScript 标准的形式语言)。说明应该尽可能完善,但可以包含待办事项和占位符。该特性需要两个实验性的实现,其中一个可以在类似 Babel 的转译器(transpiler)中实现。
下一步:从该阶段开始只接受增量调整。
阶段 3:Candidate 候选
什么是 Candidate?候选阶段,建议基本完成,此时将从实现过程和用户使用两方面获取反馈来进一步完善建议。
必备条件:规范文档必须是完整的。指定的评审人(由 TC39 而不是带头人指定)和 ECMAScript 规范的编辑须在规范上签字。还有至少要两个符合规范的实现(不必指定默认实现)。
下一步:此后,只有在实现和使用过程中出现了重大问题才会修改建议。
阶段 4:Finished 完成
什么是 Finished?建议已经准备就绪,可以添加到标准之中。
必备条件:建议进入完成阶段之前需要满足以下几点:
- Test 262 的验收测试(基本上都是 JavaScript 写的用来验证语言特性的单元测试)。
- 两个通过测试的符合规范的实现。
- 特性实现相关的重要实践经验。
- ECMAScript 规范的编辑在规范文本上的签字。
下一步: 建议将会尽快加入 ECMAScript 规范之中。当规范通过年度审核成为标准,该建议也正式成为标准的一部分。
3、别把它们称做 ECMASript 20xx 特性
综上所述,只有建议进入阶段 4,你才能确定这个特性会被纳入标准之中。因为此时它很有可能被加入到下一版本的 ECMAScript,但也不是 100% 确定(因为有可能会花更长时间)。因此,你不能称这个建议为“ES7 特性”或“ES2016 特性”。所以我喜欢把文章或博客的标题写成这样:
- “ECMAScript 建议:某特性”。在文章开头说明建议所处的阶段。
- “ES.阶段 2:某特性”。
如果建议处于阶段 4,我觉得称它 ES20xx 特性是 OK 的,但还是等规范的编辑确认该特性将会加入哪个版本之后才最保险。例如 Object.observe
就是一个已经进入阶段 2 ,却最终被撤回的 ECMAScript 建议。
4、ECMAScript 2016 将会有什么特性?
已被纳入 ES2016 的特性:
以下处于阶段 4 的特性将可能会纳入 ES2016:
- (暂时没有。)
以下处于阶段 3 的特性也许会纳入 ES2016:
- 指数运算符(Rick Waldron)
- SIMD.JS – SIMD APIs + polyfill(John McCutchan, Peter Jensen, Dan Gohman, Daniel Ehrenberg)
- 异步函数(Brian Terlson)
- Object.values/Object.entries(Jordan Harband)
- 字符串填充(Jordan Harband & Rick Waldron)
- 函数参数列表与调用中的尾逗号(Jeff Morrison)
如果你不知道某个建议特性处于哪个阶段,你可以在 ECMA-262 GitHub 库的 readme 中查阅。
5、Array.prototype.includes (Domenic Denicola, Rick Waldron)
数组的 includes
方法有如下签名:
如果传入的值在当前数组(this)中则返回 true,否则返回 false:
includes
方法与 indexOf
方法很相似——下面两个表达式是等价的:
唯一的区别是 includes()
方法能找到 NaN
,而 indexOf()
不行:
includes
不会区分 +0
和 -0
(这也与其他 JavaScript 特性表现一致):
类型数组也有 includes()
方法:
Typed Arrays will also have a method includes():
5.1 常见问题
为什么方法取名
includes
而不是contains
?
后者是最初的选择,但在 web 上将会破坏已有的代码(MooTools 在Array.prototype
上添加了 contains 方法)。为什么方法取名
includes
而不是has
?
has 通常用于键(Map.prototype.has),includes 通常用于元素(String.prototype.includes)。集合中的元素既可以被看当做 键 也可被当做 值,所以才有 Set.prototype.has (而不是 includes)。ES6 的
String.prototype.includes
方法可用于字符串,而不能用于字符。这是否和Array.prototype.includes
不一致?
如果数组和字符串的 includes 方法是相同的工作机制,那么数组的 includes 方法就应该接受数组,而不是数组元素了。不过这两个 includes 方法都参考了 indexOf 方法;字符一般是特殊情况,而任意长度的字符串则更常见。
5.2 扩展阅读
- Array.prototype.includes (Domenic Denicola, Rick Waldron)
6、指数运算符(Rick Waldron)
新提出来的特性是将 **
作为指数操作的中缀运算符:
与以下表达式运算结果相同:
示例:
扩展阅读:
- 指数运算符 (Rick Waldron)
7、SIMD.JS – SIMD APIs + polyfill (John McCutchan, Peter Jensen, Dan Gohman, Daniel Ehrenberg)
SIMD 意思是“single instruction, multiple data(单指令流多数据流)”,CPU 可以通过单条指令操作一组数据(而不是仅操作单一值),SIMD 指令集最有名的一个例子就是由 Intel 处理器的 SSE(Streaming SIMD Extensions)。
下面是简短的示例代码,请参考后面源码获取更多信息。
|
|
扩展阅读:
- SIMD.JS – SIMD APIs + polyfill (John McCutchan, Peter Jensen, Dan Gohman, Daniel Ehrenberg)
- JavaScript 可支持 SIMD (2ality)
8、异步函数(Brian Terlson)
在我介绍异步函数之前,我会先介绍如何通过 Promises 和 generators 来使用看起来同步的代码去执行异步操作。
8.1 用 Promises 和 generators 编写异步代码
对于需要异步地计算一次性结果的函数来说,Promises(ES6 的一部分)现在越来越流行。一个例子就是客户端 fetch
API,它是一种替代 XMLHttpRequest 检索文件的方案。代码如下:
|
|
co 是一个使用了 Promises 和 generators 让编程风格看起来更像异步的库,但也需要使用类似于前一个例子的风格去编码:
|
|
每次回调函数(一个 generator 函数!)产生一个 Promise 给 co,回调函数就会被挂起。一旦这个 Promise 完成,co 就会恢复该回调函数:如果 Promise 被实现,yield
会返回这个实现的值,如果被拒绝,则 yield
会抛出异常。另外,co 能处理回调函数返回的结果(与 then()
相似)。
8.2 异步函数
异步函数的语法基本上可以说是实现了 co 所做的:
|
|
在内部,异步函数的工作非常类似于 generators,但它并不会被翻译为 generator 函数。
8.3 变体
异步函数有以下几种变体:
函数声明: async function foo() { }
函数表达式:const foo = async function () {};
方法定义:let obj = { async foo() {} }
箭头函数:const foo = async () => {}
8.4.扩展阅读
- 异步函数 (Brian Terlson)
- 通过 generators 简化异步计算 (“探索 ES6”章节)
9、扩展阅读
下面是本篇博客的重要参考资料:
其他资料:
- Kangax 的 ES7 兼容性表格 会告诉你哪些建议在哪些浏览器中已经得到支持,并将建议根据所处阶段进行了分类。
- 更多关于 ES6 设计过程的信息: “ECMAScript 6 是如何被设计出来的”———“探索 ES6”中的章节。
作者:Dr. Axel Rauschmayer,时间:2015-11-15
原文链接: http://www.2ality.com/2015/11/tc39-process.html
译者:zhouweicsu