ECMAScript 2017 新特性一览

本文主要参考自 2ality 的文章:ECMAScript 2017 (ES8): the final feature set。部分章节会有修改,加入自己的一些理解与观点。阅读本文需要有一定的 ES 基础

官方公告:Standard ECMA-262

主要特性

异步函数

ES6 发布的时候,引入了 Promise 来处理异步操作,一个常见的 Async 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
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));

现在,使用 ES2017 中新增的关键字 asyncawait,我们可以实现以看起来像是同步的代码来处理异步的操作。如上面的代码可以写成:

1
2
3
4
5
6
7
8
9
10
async function fetchData(url) {
try {
let request = await fetch(url);
let text = await request.text();
return JSON.parse(text);
}
catch (err) {
console.log(`Error: ${err.stack}`);
}
}

异步函数还有一些变体,罗列如下:

  • 异步函数声明:async function foo() {}
  • 异步函数表达式:const foo = async function () {};
  • 异步方法定义:let obj = {async foo () {}};
  • 异步箭头函数:const foo = async () => {};

更多资料可参阅:tc39/ecmascript-asyncawait: Async/await for ECMAScriptSimplifying asynchronous computations via generators (section in “Exploring ES6”)

共享内存和原子

共享内存 shared memory 主要是处理并行事件对于资源调用的一种机制。

该功能引入一个新的低级别 Atomics 命名空间对象和一个 Shared Array Buffer 构造函数来作为高级别并发抽象的原始构建块。这样可以使得开发人员可以使用多个 service worker 和核心线程之间的 Shared Array Buffer 对象的数据。通过这种方式,可以更轻松的在 worker 之间进行数据共享,改善它们之间的协调性。

次要更新

Object.values/Object.entries

对象为键值对的数据结构时,每一个键值对都是一个 entryObject.entries 提供将对象转换为其可枚举的每一个对象的集合的方法。而 Object.values 提取出对象中可枚举的字符串键值属性的所有值。

Object.valuesObject.keys 功能类似。

举个例子,有一个这样的对象:foo = {a: 1, b: 2, c: 3};,上面提到的三个方法的结果分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> foo = {a:1,b:2,c:3}
< Object {a: 1, b: 2, c: 3}
> Object.values(foo)
< (3) [1, 2, 3]
> Object.keys(foo)
< (3) ["a", "b", "c"]
> Object.entries(foo)
< (3) [Array(2), Array(2), Array(2)]0: Array(2)1: Array(2)2: Array(2)length: 3__proto__: Array(0)

------
> Object.entries({ one: 1, two: 2 })
[ [ 'one', 1 ], [ 'two', 2 ] ]

> Object.entries({ [Symbol()]: 123, foo: 'abc' });
[ [ 'foo', 'abc' ] ]

字符串填充

针对 String 对象,引入了 String Padding 的规范,为字符串的处理添加了两个字符串填充的方法:padStartpadEnd

String.prototype.padStart

字符串的头部填充。接收两个参数 String.prototype.padStart(maxLength, fillString=' '),第一个参数为填充的最大长度,第二个参数为指定填充的字符串。

假设我们有一个这样的操作:s.padStart(n, f),s 为待填充的字符串,n 为填充后的长度,f 为填充的字符串。如果 f 的长度不够 n,则会重复使用,直到填充的长度达到了 n。如果 s 本身的长度已经超过 n 则不会进行填充。

String.prototype.padEnd

跟上面的方法类似,填充的位置在后面,其他处理方式一致。

Object.getOwnPropertyDescriptors

该方法是 Object.getOwnPropertyDescriptor 的复数形式:

1
2
3
4
5
6
7
function getOwnPropertyDescriptors(obj) {
const result = {};
for (let key of Reflect.ownKeys(obj)) {
result[key] = Object.getOwnPropertyDescriptor(obj, key);
}
return result;
}

旨在简化对象复制的过程,它允许装饰器能够轻松的从另一个类或对象中提取出所有描述符,并将它们分配给一个新的对象。

由于 Object.assign 方法不够完美,这个是用一种吞噬行为的方式复制对象,也就是依然会出现键值覆盖的情形。

配合 Object.create 可以实现一个对象的深拷贝:

1
const clone = obj => Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

更多 Object.getOwnPropertyDescriptor 的使用场景可以参阅:ES proposal: Object.getOwnPropertyDescriptors()

函数的拖尾逗号

以前的函数在参数定义时或者调用时,不允许出现拖尾逗号,如下面的代码:

1
2
3
4
5
6
7
8
function foo(
bar,
baz,
) { ... }
foo(
param1,
param2,
);

在之前的版本中是错误的,但是新规范中,这可以的,拖尾逗号将被忽略,如同数组和对象字面量中的拖尾逗号。

拓展阅读