Diff ECMAScript 2019

ECMAScript® 2019 已定稿,通过 ECMA 链接可以查看到 ECMAScript 第十版语言标准的所有内容。

由于该语言于几年前开始的年更节奏,每年的变化并不如当初 ES5 到 ES6 的改动大,于是想知道标准有哪些改动,看看在今年发布期内完结的提案倒是更有帮助些。

提案 作者 主席 TC39 会议纪要 预期发布年份
Optional catch binding Michael Ficarra Michael Ficarra May 2018 2019
JSON superset Richard Gibson Mark Miller
Mathias Bynens
May 2018 2019
Symbol.prototype.description Michael Ficarra Michael Ficarra November 2018 2019
Function.prototype.toString revision Michael Ficarra Michael Ficarra November 2018 2019
Object.fromEntries Darien Maillet Valentine Jordan Harband
Kevin Gibbons
January 2019 2019
Well-formed JSON.stringify Richard Gibson Mathias Bynens January 2019 2019
String.prototype.{trimStart,trimEnd} Sebastian Markbåge Sebastian Markbåge
Mathias Bynens
January 2019 2019
Array.prototype.{flat,flatMap} Brian Terlson
Michael Ficarra
Mathias Bynens
Brian Terlson
Michael Ficarra
January 2019 2019

接下来我们一一细读。其中,部分新特性我在赶上 ECMAScript 潮流:用现代 JavaScript 编程一文中已给出详例解释,故此处针对剩余六个提案/特性进行介绍。

1. JSON superset

什么是 JSON 超集?还记得 这个符号的可以这样解释该提案 JSON ⊂ ECMAScript,简而言之就是让 ECMAScript 兼容所有 JSON 支持的文本。ECMAScript 曾在标准 JSON.parse 部分阐明 JSON 确为其一个子集,但由于 JSON 内容可以正常包含 U+2028 行分隔符与 U+2029 段落分隔符而 ECMAScript 却不行。

该草案旨在解决这一问题。在这之前,如果你使用 JSON.parse() 执行带如上特殊字符的字符串时,只会收到 SyntaxError 的错误提示。该草案同样是向后兼容的,其对用户唯一的影响是保持原样,即在暂不支持特殊字符解析的运行环境中保持 SyntaxError 的报错。

2. Symbol.prototype.description

ECMAScript 在该提案中规定,Symbol.prototype.description 是一个访问器属性,你可以通过它获取 Symbol 对象的字符串表述,而在此之前,你必须通过调用 Symbol.prototype.toString 方法达到同样的目的。我们来看几个例子熟悉一下:

const testSymbol = Symbol('Test')
testSymbol.description // 'Test'

Symbol("foo") + "bar";      
// TypeError: Can't convert symbol to string

Symbol("foo").toString() + "bar"
// "Symbol(foo)bar"

3. Function.prototype.toString

这是一个校订提案,内容很长详见 https://tc39.es/Function-prototype-toString-revision/,但解释起来相对容易。我们知道,调用 Function 原型链上的 toString() 方法可以返回函数的源码字符串,但是在转换过程中,空格、代码注释等内容会被去除。

在校订中,这些内容得以正常解析保留,使得调用 toString() 方法获得的结果与函数的实际定义更加接近,来看个例子加深印象:

function /* this is bar */ bar () {
  // Hello
  return 'Hello, bar!';
}

bar.toString()
// → "function /* this is bar */ bar () {
// →   // Hello
// →   return 'Hello, bar!';
// → }"

4. Object.fromEntries

在 JavaScript 操作中,数据在各种数据结构之间的转换都是很容易的,比如 Map 到数组、Map 到 Set、对象到 Map 等等:

const map = new Map().set('foo', true).set('bar', false);
const arr = Array.from(map);
const set = new Set(map.values());

const obj = { foo: true, bar: false };
const newMap = new Map(Object.entries(obj));

其中,Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,例如:

const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]

但是如果我们需要将一个键值对列表转换为对象,相对还是很麻烦的:

const map = new Map().set('foo', true).set('bar', false);

const obj = Array.from(map).reduce((acc, [ key, val ]) => {
  return Object.assign(acc, { 
    [key]: val 
  });
}, {});

该提案的目的在于为对象添加一个新的静态方法 Object.fromEntries,用于将符合键值对的列表(例如 Map、数组等)转换为一个对象。如此一来,以上转换我们只需要一行代码即可搞定:

Object.fromEntries(map);

5. JSON.stringify

当你使用 JSON.stringify 处理一些无法用 UTF-8 编码表示的字符时(U+D800 至 U+DFFF),曾经返回的结果会是一个乱码 Unicode 字符,即“�”。该提案提出,用 JSON 转义序列来安全的表示这些特殊字符。

正常字符的表示不变:

JSON.stringify('𝌆')
// → '"𝌆"'
JSON.stringify('\uD834\uDF06')
// → '"𝌆"'

而无法用 UTF-8 编码表示的字符会被序列化为转移序列:

JSON.stringify('\uDF06\uD834')
// → '"\\udf06\\ud834"'
JSON.stringify('\uDEAD')
// → '"\\udead"'

6. Array.prototype.{flat,flatMap}

这个提案提出了两个方法,其中:

  • Array.prototype.flat 返回一个新数组,其中所有子数组元素会以指定的深度递归的连接到一起;
  • Array.prototype.flatMap 方法首先会调用提供的函数执行一次 map() 方法,然后再通过类似 flat 方法「打平」数组。它等价于执行完 map() 后再执行一次 flat() 方法,但当你执行 map() 时返回的结果如果是个数组时,这个方法会显得额外有用与简便;

来看几个例子解释一下,首先 flat() 方法支持不同深度的「打平」,其中 “Infinity” 可以将所有深度打平成一级:

['Dog', ['Sheep', ['Wolf']]].flat()
//[ 'Dog', 'Sheep', [ 'Wolf' ] ]

['Dog', ['Sheep', ['Wolf']]].flat(2)
//[ 'Dog', 'Sheep', 'Wolf' ]

['Dog', ['Sheep', ['Wolf']]].flat(Infinity)
//[ 'Dog', 'Sheep', 'Wolf' ]

用另一个例子来解释 flatMap() 方法的便利之处:

['My dog', 'is awesome'].map(words => words.split(' '))
//[ [ 'My', 'dog' ], [ 'is', 'awesome' ] ]

['My dog', 'is awesome'].flatMap(words => words.split(' '))
//[ 'My', 'dog', 'is', 'awesome' ]

参考

Leave a Comment