ECMAScript 2016/2017/2018新特性详解

ECMAScript 2016/2017/2018新特性详解

作者|rajaraodv

译者|无明

出处丨前端之巅

要跟踪 JavaScript(ECMAScript)究竟提供了哪些新特性并不容易,找到有用的代码示例更加困难。在本文中,我将介绍在 ES2016、ES2017 和 ES2018 中添加的 18 个特性,并提供有用的示例。

ECMAScript 2016

1. Array.prototype.includes

includes 是 Array 的一个方法,用于查找数组中是否包含了某个项(与 indexOf 不同的是,它可以包含 NaN)。

ECMAScript 2016/2017/2018新特性详解


2. 指数中缀运算符

加法和减法等数学运算分别有 + 和 - 等中缀运算符。类似的,中缀运算符通常用于指数运算。ECMAScript 2016 引入了,用来替代 Math.pow。

ECMAScript 2016/2017/2018新特性详解


ECMAScript 2017

1. Object.values()

Object.values() 是一个与 Object.keys() 类似的新函数,它会返回 Object 所有属性的值,但不包括原型链中的值。

ECMAScript 2016/2017/2018新特性详解


2. Object.entries()

Object.entries() 与 Object.keys 相关,但它不仅返回键,而是以数组的方式返回键和值。这使得在循环中使用对象或将对象转换为 Map 等操作变得非常简单。

例 1:

ECMAScript 2016/2017/2018新特性详解


例 2:

ECMAScript 2016/2017/2018新特性详解


3. 字符串填充

String 新增了两个实例方法——String.prototype.padStart 和 String.prototype.padEnd——允许将空字符串或其他字符串追加或前置到原始字符串的尾部或开头。

'someString'.padStart(numberOfCharcters [,stringForPadding]); 
'5'.padStart(10) // ' 5'
'5'.padStart(10, '=*') //'=*=*=*=*=5'
'5'.padEnd(10) // '5 '
'5'.padEnd(10, '=*') //'5=*=*=*=*='

当我们想要在终端对齐要打印的东西,这会派上用场。

3.1 padStart 示例:

在下面的示例中,我们有一些不同长度的数字。我们想在所有数字前面填充“0”,让它们都变成 10 位数,方便显示。我们可以使用 padStart(10, '0') 轻松实现这一目的。

ECMAScript 2016/2017/2018新特性详解


3.2 padEnd 示例:

当我们打印不同长度的多个项目并希望它们能够对齐时,使用 padEnd 真的很方便。

下面是 padEnd、padStart 和 Object.entries 组合在一起产生漂亮输出的一个很好的现实示例。

ECMAScript 2016/2017/2018新特性详解


3.3 对 Emojis 和其他双字节字符使用 padStart 和 padEnd

Emojis 和其他双字节字符使用多个 unicode 字节表示,因此 padStart 和 padEnd 可能无法按预期工作!

例如:假设我们尝试使用❤️填充字符串 heart,让它达到 10 个字符。结果如下所示:

// 请注意,我们看到的不是 5 个心, 而是两个心和一个看起来有点怪的另一个心!
'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

这是因为❤️占用 2 个码位(’\\u2764\\uFE0F’)!heart 本身是 5 个字符,所以我们只剩下 5 个字符来填充。JS 使用'\\u2764\\uFE0F'来填充两颗心,生成❤️❤️。对于最后一个,它使用心的第一个字符\\u2764 生成❤。

所以我们最终得到:❤️❤️❤heart。

关于 unicode 字符的转换,可以参考:

https://encoder.internetwache.org/#tab_uni

4. Object.getOwnPropertyDescriptors

这个方法返回给定对象所有属性的详细信息(包括 get 和 set 方法)。添加它的主要动机是允许将一个对象浅复制 / 克隆到另一个对象,同时也复制 getter 和 setter 函数而不是 Object.assign。

Object.assign 将浅复制除原始源对象的 getter 和 setter 函数之外的所有东西。

下面的示例显示了使用 Object.assign 和 Object.getOwnPropertyDescriptors 以及 Object.defineProperties 将原始对象 Car 复制到新对象 ElectricCar 的区别。你将看到,通过使用 Object.getOwnPropertyDescriptors,discount 的 getter 和 setter 函数也会被复制到目标对象中。

之前:

ECMAScript 2016/2017/2018新特性详解


之后:

ECMAScript 2016/2017/2018新特性详解


5. 在函数参数中添加尾逗号

这是一个次要更新,允许最后一个函数参数后面存在逗号。

以下示例显示了问题和解决方案。

ECMAScript 2016/2017/2018新特性详解


注意:你也可以使用尾逗号调用函数!

6. async/await

到目前为止,这是最重要和最有用的一个特写。异步函数可以避免回调地狱,并让整个代码看起来更简单。

async 关键字告诉 JavaScript 编译器要以不同的方式处理这个函数。在遇到函数中的 await 关键字时,编译器就会暂停。它假定 await 之后的表达式会返回一个 promise 并等待,直到 promise 完成或被拒绝。

在下面的示例中,getAmount 函数调用两个异步函数 getUser 和 getBankBalance。我们可以使用 promise,但使用 async await 更加优雅和简单。

ECMAScript 2016/2017/2018新特性详解


6.1 返回 promise 的异步函数

如果你想获得异步函数返回的结果,需要使用 Promise 的 then 语法来捕获结果。

在下面的示例中,我们希望使用 console.log 来记录结果(但不在 doubleAndAdd 中)。所以,我们需要等待,并使用 then 语法将结果传给 console.log。

ECMAScript 2016/2017/2018新特性详解


6.2 并行调用 async/await

在前面的例子中,我们调用 await 两次,每次等待一秒钟(总共 2 秒)。其实我们可以并行调用 await,因为 a 和 b 没有相互依赖。

ECMAScript 2016/2017/2018新特性详解


6.3 async/await 函数的错误处理

使用 async/await 时,有多种方法可以处理错误。

选项 1——在函数中使用 try catch

ECMAScript 2016/2017/2018新特性详解


选项 2——捕获每个 await 表达式

由于每个 await 表达式都返回一个 Promise,因此可以像下面这样捕获每行的错误。

ECMAScript 2016/2017/2018新特性详解


选项 3——捕获整个 async-await 函数

ECMAScript 2016/2017/2018新特性详解


ECMAScript 2018

1. 共享内存和原子

这是一个高级特写,是 JS 引擎的核心增强。

主要的想法是将某种多线程特写引入到 JavaScript 中,让 JS 开发人员可以自己管理内存,编写高性能的并发程序。

这是通过一个叫作 SharedArrayBuffer 的全局对象来实现的,这个对象实质上将数据存储在共享内存中。因此,这些数据可以在主 JS 线程和 Web 工作线程之间共享。

到目前为止,如果我们想在主 JS 线程和 Web 工作者之间共享数据,必须复制数据,并使用 postMessage 将数据发送给另一个线程。但以后不需要这样了!

你只需使用 SharedArrayBuffer,主线程和多个 Web 工作线程就可以立即访问共享数据。

但在线程之间共享内存会导致竞态条件。为了避免竞态条件,引入了“Atomics”全局对象。Atomics 提供了各种方法来在线程使用数据时锁定共享内存,还提供了安全更新共享内存数据的方法。

建议通过某个库使用这个特性,但是现在还没有基于该特性而构建的库。

2. 移除了标记模板字面量限制

首先,我们需要澄清“标记模板字面量”是什么,以便更好地理解这个特性。

在 ES2015+ 中,有一个叫作标记模板字面量的特性,允许开发人员自定义字符串的插值方式。例如,如果使用标准方式,字符串插入如下所示:

ECMAScript 2016/2017/2018新特性详解


在标记的字面量中,你可以编写一个函数来接收硬编码的字符串字面量(例如 [ 'Hello ', '!' ])和替换变量(例如 ['Raja']),它们作为传给自定义函数(例如 greet)的参数,并从自定义函数中返回你想要的任何内容。

下面的例子显示了我们的自定义函数 greet 根据一天的时间将“早上好!”“下午好”等添加到字符串字面量中,并返回自定义字符串。

ECMAScript 2016/2017/2018新特性详解


现在我们介绍了什么是“标记”函数,很多人想要将这个特性用在不同的地方,例如命令行终端和为 HTTP 请求编写 URI,等等。

标记字符串字面量的问题

问题是 ES2015 和 ES2016 规范不允许使用转义字符,如“\\u”(unicode)、“\\x”(十六进制),除非它们看起来像\\u00A9或\\u{2F804}或\\xA9 这样。

如果你有一个标记函数在内部使用了其他领域的规则(如终端的规则),可能需要使用类似\\ubla123abla 这样的字符,那么你将会得到一个语法错误。

在 ES2018 中,规则有所放宽,只要标记函数通过一个具有“cooked”属性(无效字符为“undefined”)和“raw”属性(你想要的东西)的对象来返回值,可以允许这种无效的转义字符。

function myTagFunc(str) { 
return { "cooked": "undefined", "raw": str.raw[0] }
}
var str = myTagFunc `hi \\ubla123abla`; //call myTagFunc
str // { cooked: "undefined", raw: "hi \\\unicode" }

3. 正则表达式的“.”标志

目前,在 RegEx 中,虽然点号(“.”)可以匹配单个字符,但不能匹配\n、\r、\n 等换行符。

例如:

//Before
/first.second/.test('first\nsecond'); //false

这个增强使点号可以匹配任何单个字符。为了正常使用这个特性,在创建 RegEx 时需要使用\s 标志。

//ECMAScript 2018
/first.second/s.test('first\nsecond'); //true Notice: /s

以下是提案文档中的 API 梗概:

ECMAScript 2016/2017/2018新特性详解


4. RegExp 命名组捕获

这个增强从其他语言(如 Python、Java 等)中引入了一个有用的 RegExp 特性,叫作“命名组”。开发人员可以为 RegExp 组的不同部分指定名字(标识符),格式为 (?…)。然后,他们可以使用标识符轻松获取他们需要的组。

4.1 基本命名组示例

在下面的示例中,我们使用 (?)、(?) 和 (?) 分别对应日期 RegExp 组的不同部分。结果对象将包含 groups 属性,这个属性又包含带有相应值的 year、month 和 day 属性。

ECMAScript 2016/2017/2018新特性详解


4.2 在正则表达式中使用命名组

我们可以使用\k来反向引用正则表达式中的组,请看下面的示例。

ECMAScript 2016/2017/2018新特性详解


4.3 在 String.prototype.replace 中使用命名组

命名组特性现在可以被用在 String 的 replace 实例方法中,我们可以轻松地交换字符串中的单词。

例如,将“firstName,lastName”更改为“lastName,firstName”。

ECMAScript 2016/2017/2018新特性详解


5. 对象的剩余属性

剩余运算符...(三个点)允许我们提取尚未提取的 Object 属性。

5.1 你可以使用剩余操作符来提取你所需要的属性

ECMAScript 2016/2017/2018新特性详解


5.2 你还可以移除不需要的项!

ECMAScript 2016/2017/2018新特性详解


6. 对象的延展属性

延展属性看起来与剩余属性…有点像,不同之处在于,你用它来创建新对象。

提示:延展运算符被用在等号的右侧,而剩余运算符被用在等号的左侧。

ECMAScript 2016/2017/2018新特性详解


7. RegExp 后行断言

这是对 RegEx 的一种增强,用于确保一些字符串紧靠在其他字符串之前。

你现在可以使用组 (?<=…)(问号、小于号、等号)来查找正向断言。

此外,你可以使用 (?<!…)(问号、小于号、感叹号)来查找负向断言。

正向断言:假设我们要确保 # 符号存在于 winning 之前(即:#winning),并希望正则表达式只返回字符串“winning”,如下所示。

ECMAScript 2016/2017/2018新特性详解


负向断言:假设我们想要从行中提取以€符号(而不是 $ 符号)为前缀的数字。

ECMAScript 2016/2017/2018新特性详解


8. RegExp Unicode 属性转义

编写 RegExp 以匹配各种 unicode 字符并不容易。\w、\W、\d 等只匹配英文字符和数字,那么其他语言(如印地语、希腊语等)中的数字该怎么办?

这就是 Unicode 属性转义的用武之地。事实证明,Unicode 为每个符号(字符)添加了元数据属性,并使用它来分组或表征各种符号。

例如,Unicode 数据库将所有印地语字符(हिन्दी)归为一个叫作 Script 的属性之下,值为 Devanagari,另一个属性为 Script_Extensions,值为 Devanagari。所以我们可以通过搜索 Script = Devanagari 获得所有的印地语字符。

Devanagari 可以被用于各种印度语言,如马拉地语、印地语、梵语等。

从 ECMAScript 2018 开始,我们可以使用\p 以及{Script = Devanagari}来转义字符,以匹配所有印度字符。也就是说,我们可以在 RegEx 中使用\p{Script = Devanagari}来匹配所有梵文字符。

ECMAScript 2016/2017/2018新特性详解


同样,Unicode 数据库将所有希腊字符归到值为 Greek 的 Script_Extensions(和 Script)属性下。所以,我们可以使用 Script_Extensions = Greek 或 Script = Greek 搜索所有希腊字符。

也就是说,我们可以在 RegEx 中使用\p{Script = Greek}来匹配所有希腊字符。

ECMAScript 2016/2017/2018新特性详解


此外,Unicode 数据库将各种类型的 Emojis 归到布尔类型属性 Emoji、Emoji_Component、Emoji_Presentation、Emoji_Modifier 和 Emoji_Modifier_Base 之下,属性值为“true”。因此,我们只需使用 Emoji 即可搜索所有表情符号。

也就是说,我们可以使用\p{Emoji}、\Emoji_Modifier 等来匹配各种表情符号。

下面是一个很清晰的示例。

ECMAScript 2016/2017/2018新特性详解


最后,我们可以使用大写的“P”(\P,而不是小写的\p)来转义字符,进行负向匹配。

9. Promise.prototype.finally()

finally() 是 Promise 新增的一个实例方法,允许在 resolve 或 reject 之后运行回调,以便清理相关资源。finally 回调没有任何参数,而且无论如何都会被执行。

我们来看看各种情况。

ECMAScript 2016/2017/2018新特性详解


ECMAScript 2016/2017/2018新特性详解


ECMAScript 2016/2017/2018新特性详解


10. 异步迭代

这是一个非常有用的特性。基本上,它让我们可以更轻松地创建异步代码循环!

这个特性添加了一个新的“for-await-of”循环,允许我们在循环中调用返回 promise(或 promise 数组)的异步函数。循环在执行下一步之前会等待每个 Promise 完成。

ECMAScript 2016/2017/2018新特性详解


英文原文

https://medium.freecodecamp.org/here-are-examples-of-everything-new-in-ecmascript-2016-2017-and-2018-d52fa3b5a70e