作者:Dr. Axel Rauschmayer,时间:2015-11-15
原文链接: http://www.2ality.com/2015/11/tc39-process.html
译文链接: http://zhouweicsu.github.io/mobile/2015/12/04/tc39-process/
本文回答了关于下一版 JavaScript(ECMAScript 2016)的以下几个问题:
本文会持续更新以及时反馈最新进展。
答:TC39 (Technical Committee 39)。
TC39 是推进 JavaScript 发展的委员会。其会员都是公司(其中主要是浏览器厂商)。TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。会议纪录都可在网上查看,可以让你对 TC39 如何工作有一个清晰的概念。
有时候(实际上本文就如此),你会发现 TC39 会员这个词被用来指代一个人,其实指的是 TC39 会员公司派出的代表。
很有意思的是,TC39 实行的是协商一致的原则:通过一项决议必须得到每一位会员(公司代表)的赞成。
最近发布的 ECMAScript(ES6)新增内容很多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度如此之大主要有两大原因:
因此,从 ECMAScript 2016(ES7)开始,版本发布将会变得更加频繁,每年发布一个新版本,这么一来新增内容也会更小。新版本将会包含每年截止时间之前完成的所有特性。
每个 ECMAScript 特性的建议将会从阶段 0 开始, 然后经过下列几个成熟阶段。其中从一个阶段到下一个阶段必须经过 TC39 的批准。
什么是 Strawman?一个推进 ECMAScript 发展的自由形式的想法。该想法必须由 TC39 的会员提交,如果是非会员则必须注册成为 TC39 贡献者才能提交。
必备条件:文件必须在 TC39 的会议上通过审议(原文),然后才能加入阶段 0 的建议页面。
什么是 Proposal?一份新特性的正式建议文档。
必备条件:必须确定一位带头人来为负责这份建议。无论是带头人或者联合带头人都必须是 TC39 的会员(原文)。建议要解决的问题必须以简明的文字描述,而解决方案则要给出相应的实例和 API,并详细描述语义及算法。最后,必须指明此建议的潜在问题,例如与其他特性之间的关联,实现难点等。实现方式,polyfills 和 demo 也是需要的。
下一步:通过一个阶段 1 的建议,表明 TC39 愿意研究、讨论并促成该建议。接下来,我们就可以期待该建议的重大改变了。
什么是 Draft?草案是规范的第一个版本。其与最终标准中包含的特性不会有太大差别。
必备条件:建议此时必须要附加该特性的语法和语义的正式说明(使用 ECMAScript 标准的形式语言)。说明应该尽可能完善,但可以包含待办事项和占位符。该特性需要两个实验性的实现,其中一个可以在类似 Babel 的转译器(transpiler)中实现。
下一步:从该阶段开始只接受增量调整。
什么是 Candidate?候选阶段,建议基本完成,此时将从实现过程和用户使用两方面获取反馈来进一步完善建议。
必备条件:规范文档必须是完整的。指定的评审人(由 TC39 而不是带头人指定)和 ECMAScript 规范的编辑须在规范上签字。还有至少要两个符合规范的实现(不必指定默认实现)。
下一步:此后,只有在实现和使用过程中出现了重大问题才会修改建议。
什么是 Finished?建议已经准备就绪,可以添加到标准之中。
必备条件:建议进入完成阶段之前需要满足以下几点:
下一步: 建议将会尽快加入 ECMAScript 规范之中。当规范通过年度审核成为标准,该建议也正式成为标准的一部分。
综上所述,只有建议进入阶段 4,你才能确定这个特性会被纳入标准之中。因为此时它很有可能被加入到下一版本的 ECMAScript,但也不是 100% 确定(因为有可能会花更长时间)。因此,你不能称这个建议为“ES7 特性”或“ES2016 特性”。所以我喜欢把文章或博客的标题写成这样:
如果建议处于阶段 4,我觉得称它 ES20xx 特性是 OK 的,但还是等规范的编辑确认该特性将会加入哪个版本之后才最保险。例如 Object.observe
就是一个已经进入阶段 2 ,却最终被撤回的 ECMAScript 建议。
已被纳入 ES2016 的特性:
以下处于阶段 4 的特性将可能会纳入 ES2016:
以下处于阶段 3 的特性也许会纳入 ES2016:
如果你不知道某个建议特性处于哪个阶段,你可以在 ECMA-262 GitHub 库的 readme 中查阅。
数组的 includes
方法有如下签名:
Array.prototype.includes(value:任意值): boolean
如果传入的值在当前数组(this)中则返回 true,否则返回 false:
> ['a', 'b', 'c'].includes('a')
true
> ['a', 'b', 'c'].includes('d')
false
includes
方法与 indexOf
方法很相似——下面两个表达式是等价的:
arr.includes(x)
arr.indexOf(x) >= 0
唯一的区别是 includes()
方法能找到 NaN
,而 indexOf()
不行:
> [NaN].includes(NaN)
true
> [NaN].indexOf(NaN)
-1
includes
不会区分 +0
和 -0
(这也与其他 JavaScript 特性表现一致):
> [-0].includes(+0)
true
类型数组也有 includes()
方法:
Typed Arrays will also have a method includes():
let tarr = Uint8Array.of(12, 5, 3);
console.log(tarr.includes(5)); // true
为什么方法取名 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 方法;字符一般是特殊情况,而任意长度的字符串则更常见。
新提出来的特性是将 **
作为指数操作的中缀运算符:
x ** y
与以下表达式运算结果相同:
Math.pow(x, y)
示例:
let squared = 3 ** 2; // 9
let num = 3;
num **= 2;
console.log(num); // 9
扩展阅读:
SIMD 意思是“single instruction, multiple data(单指令流多数据流)”,CPU 可以通过单条指令操作一组数据(而不是仅操作单一值),SIMD 指令集最有名的一个例子就是由 Intel 处理器的 SSE(Streaming SIMD Extensions)。
下面是简短的示例代码,请参考后面源码获取更多信息。
var a = SIMD.float32x4(1.0, 2.0, 3.0, 4.0);
var b = SIMD.float32x4(5.0, 6.0, 7.0, 8.0);
var c = SIMD.float32x4.add(a,b);
扩展阅读:
在我介绍异步函数之前,我会先介绍如何通过 Promises 和 generators 来使用看起来同步的代码去执行异步操作。
对于需要异步地计算一次性结果的函数来说,Promises(ES6 的一部分)现在越来越流行。一个例子就是客户端 fetch
API,它是一种替代 XMLHttpRequest 检索文件的方案。代码如下:
function fetchJson(url) {
return fetch(url)
.then(request => request.text())
.then(text => {
return JSON.parse(text);
})
.catch(error => {
console.log(`ERROR: ${error.stack}`);
});
}
fetchJson('http://example.com/some_file.json')
.then(obj => console.log(obj));
co 是一个使用了 Promises 和 generators 让编程风格看起来更像异步的库,但也需要使用类似于前一个例子的风格去编码:
const fetchJson = co(function* () {
try {
let request = yield fetch(url);
let text = yield request.text();
return JSON.parse(text);
}
catch (error) {
console.log(`ERROR: ${error.stack}`);
}
});
每次回调函数(一个 generator 函数!)产生一个 Promise 给 co,回调函数就会被挂起。一旦这个 Promise 完成,co 就会恢复该回调函数:如果 Promise 被实现,yield
会返回这个实现的值,如果被拒绝,则 yield
会抛出异常。另外,co 能处理回调函数返回的结果(与 then()
相似)。
异步函数的语法基本上可以说是实现了 co 所做的:
async function fetchJson(url) {
try {
let request = await fetch(url);
let text = await request.text();
return JSON.parse(text);
}
catch (error) {
console.log(`ERROR: ${error.stack}`);
}
}
在内部,异步函数的工作非常类似于 generators,但它并不会被翻译为 generator 函数。
异步函数有以下几种变体:
函数声明: async function foo() { } 函数表达式:const foo = async function () {}; 方法定义:let obj = { async foo() {} } 箭头函数:const foo = async () => {}
下面是本篇博客的重要参考资料:
其他资料:
扫码关注w3ctech微信公众号
共收到0条回复