ES2019中的新特性

文本为翻译计划的第一篇文章,原文地址

ES2019最近已完成制定,这意味着JavaScript将有很多新特性供大家学习。这些特性目前可以在Chrome 73中使用。

  • Array#{flat, flatMap}
  • Object.fromEntries
  • String#{trimStart, trimEnd}
  • Symbol#description
  • try {} catch {} optional binding
  • JSON superset
  • well-formed JSON.stringify
  • stable Array#sort
  • revised Function#toString

遗憾的是,在写此文的时候还没有基于V8 7.3的Node.js版本,目前的Node.js 12 仍然是基于V8 7.1。然而,V8 v7.0已经包含了ES2019的大部分特性,如下图所示。本文中我们仍然使用Node.js 11.6(V8 v7.0.276)。

Array#flat()Array#flatMap()

flat()flatMap()在Chrome 69 和 V8 6.9 中是可用的,因此你需要 Node.js 11。

Array#flat()跟Lodash的_.flattenDepth()方法类似。flat()方法接受一组嵌套数组,并将该嵌套数组扁平化。

1
[[1, 2], [3, 4], [5, 6]].flat(); // [1, 2, 3, 4, 5, 6]

默认情况下,flat()仅扁平化一层:

1
2
// [1, 2, 3, 4, 5, 6]
[[[1, 2]], [[3, 4]], [[5, 6]]].flat(2);

但是,flat()接收一个表示深度的参数来确定你想要扁平化的深度。

1
2
// [1, 2, 3, 4, 5, 6]
[[[1, 2]], [[3, 4]], [[5, 6]]].flat(2);

新的flatMap()方法相当于在使用map()之后调用flat()。如果你的map()返回一个数组,这将非常方便。

1
2
3
4
const oddNumbers = [1, 3, 5, 7, 9];
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
const allNumbers = oddNumbers.flatMap(num => ([num, num + 1]));

因为flatMap()的回调参数返回了一个数组,flatMap()扁平化了该数组。上面的方法类似于:

1
const allNumbers = oddNumbers.map(num => ([num, num + 1])).flat();

使用flatMap()时有一个优雅的小技巧,你可以一步实现filter()map()。 你可以在flatMap()回调中通过返回一个空数组过滤掉一个参数。

1
2
// [ 1, 3, 5, 7, 9 ]
const oddNumbers = allNumbers.flatMap(num => num % 2 === 0 ? [] : num);

Object.fromEntries()

Object.fromEntries()方法目前在所有版本的Node.js中都无法使用,你可以npm安装 object.fromentriespolyfill。

Object.fromEntries()试图使Map到Object的转换变得简单。

1
2
Object.fromEntries(new Map([['hello', 'world'], ['foo', 'bar']]));
// // { hello: 'world', foo: 'bar' }

一个灵巧的副作用是你可以把键值对数组转化为JavaScipt对象。

1
2
Object.fromEntries([['hello', 'world'], ['foo', 'bar']]);
// { hello: 'world', foo: 'bar' }

该方法的主要功能是将map类型转换为object类型,但是键值对的转换仍然是有帮助的。

例如,MangoDB 不允许用.符号来存储key。在MangoDB中存储带有.的key时要使用键值对数组。 Object.fromEntries()和它的反函数 Object.entries()使对象与键值对数组之间的转换变得容易了很多。

1
2
3
4
5
6
7
8
9
10
const packages = { 'lodash.get': '4.x', 'object.fromentries': '2.x' };
// Stores `[ [ 'lodash.get', '4.x' ], [ 'object.fromentries', '2.x' ] ]`
await mongoose.model('Project').create({ packages: Object.entries(packages) });
await mongoose.model('Project').findOne().then(doc => {
// Convert `[ [ 'lodash.get', '4.x' ], [ 'object.fromentries', '2.x' ] ]`
// back into a native JavaScript object.
return Object.assign(doc, { packages: Object.fromEntries(doc.packages) });
});

Symbol#description

Symbol 首次出现在ES2015中,用来解决属性间的命名冲突。一个Symbol对象内的属性名都是独一无二的。

1
2
3
4
5
const sym1 = Symbol('foo');
const sym2 = Symbol('foo');
const obj = { [sym1]: 'bar', [sym2]: 'baz' };
console.log(obj[sym1]); // 'bar'
console.log(obj[sym2]); // 'baz'

Symbol()的第一个参数被称为description。description不是id,多个symbol对象可以拥有相同的description并且互相之间没有冲突。symbol对象的description完全是为了调试功能。

在ES2019之前,如果你想访问symbol对象的description,只能使用.tostring()

1
2
3
const sym = Symbol('foo');
sym.toString(); // Symbol('foo')

在 Node.js 11中你可以这么访问:

1
sym.description; // foo

description属性是不可写的。

1
2
3
sym.description; // foo
sym.description = 'bar';
sym.description; // still "foo"

可选的catch绑定

在Node.js 8中,使用try/catch时你总是需要在catch中声明一个变量,即使你用不到该变量。

1
2
3
4
5
try {
// ...
} catch (err) {
// Need to specify `err`, even if it isn't used.
}

ES2019中可选的catch绑定能让你跳过在catch中定义变量。可选的catch绑定在Node.js 10和Chrome 66以上版本中是可用的。

1
2
3
4
5
try {
// ...
} catch {
// No `err` variable
}