每一个 JavaScript 开发者都该了解的 ES2018 新特性

原文做者:Faraz Kelhinijavascript

译者:UC 国际研发 Jothycss


写在最前:欢迎你来到“UC国际技术”公众号,咱们将为你们提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。前端

编者按:曾几什么时候,年少的我捧着阮一峰老师的《ES6 标准入门》,感叹 JS 变迁实在太快,好怕学不动了。直至写了几年 ES6 的今日,回头看方知:不要为了学 ES X 而学 ES X,不管 ES 几其实都是语法糖,是辅助角色,重点是想清楚它能为咱们的开发带来什么好处,而不是本末倒置。今天介绍的 ES2018 新特性仍是有蛮多亮点的,一块儿来看看吧。java


ECMAScript 标准的第九版,官宣为 ECMAScript 2018(或简称 ES2018),已于 2018 年 6 月发布。从 ES2016 开始,每隔一年就会发布一版 ECMAScript 规范的新版本,并添加很少于主版本的功能。 最新的这个版本延续了每一年发布的周期,新增了四个新的 RegExp 特性,rest/spread 属性,异步迭代和 Promise.prototype.finally。 此外,它还从标记模板中删除了转义序列的语法限制。
git

咱们将在后面的小节中详细解释这些新变化 😉。程序员



rest/spread 属性

回顾 ES2015,最有趣的功能当数 spread 运算符。 该运算符极大简化了数组的复制及合并。你可使用它替换concat()slice()方法:github


在必须将数组的各个项分别做为参数传入函数的状况下,扩展运算符也派得上用场。 例如:正则表达式


经过向对象文法添加 spread 属性,ES2018 进一步扩展了此语法。 使用 spread 属性,你能够将对象自身的可枚举属性复制到新对象上。举个例子 🌰:算法

在此代码中,... 运算符用于检索 obj1 的属性并将它们分配给 obj2。 在 ES2018 以前,这么作会报错的。 若是出现多个属性同名的状况,将会取最后一个值:编程


Spread 属性还提供了一种合并两个或多个对象的新方法,能够替代 Object.assign() 方法使用:

在此代码中,Object.assign() 方法会执行其继承的 setter 属性,而 spread 属性则彻底忽略了这一步。

切记!spread 属性只复制可枚举属性。 在下面的例子中,type 属性不会出如今复制出的对象中,由于其enumerable 属性为 false

继承属性即便是可枚举的,也会被忽略:

在这段代码中,car2 继承了carcolor 属性。 因为 spread 仅复制对象自身的属性,所以返回值中不包含 color 属性。

请记住,spread 只是对象的浅复制。 若是属性中包含对象,则仅复制对象的引用:

copy1 中的 x 属性与copy2中的x 属性引用了内存中同一个对象,所以严格等于(strict equality)运算符返回 true.

ES2015 新增的另外一有用功能是 rest 参数,它使 JavaScript程序员可以使用...将值表示为数组。 例如:

arr 的第一项被赋值给 x,剩下的被赋值给 rest 变量。 这种名为数组解构的模式很是受欢迎,以致于 Ecma 技术委员会(Ecma Technical Committee)决定为对象带来相似的功能:

此代码使用 rest 属性解构赋值,将对象 obj 剩余的自身可枚举属性复制到新对象rest中。 须要引发注意的是,rest 属性必须始终位于对象的末尾,不然会报错:

此外,在对象中使用多个 rest 语法也会报错,除非它们是嵌套使用的:


Rest/Spread 属性支持

Node.js:
  • 8.0.0(须要 --harmony 运行时 flag)

  • 8.3.0(彻底支持)


异步迭代

迭代数据集是编程的重要组成部分。 在 ES2015 以前,JavaScript提供了for,for...inwhile 等语句,以及map(),filter()forEach() 等方法。 为了方便程序员一个个地处理集合元素,ES2015 引入了迭代器接口。

若是对象具备 Symbol.iterator 属性,则表示它是可迭代的。 在 ES2015 中,字符串和集合对象(如Set, MapArray)带有Symbol.iterator 属性,所以是可迭代的。 如下代码说明了如何每次访问一个可迭代元素☝️:

Symbol.iterator 是个广为人知的符号,用于表示返回迭代器的函数。与迭代器交互主要使用next()方法。此方法返回一个具备valuedone 两个属性的对象。 value 属性包含集合中下一个元素的值。done 属性包含truefalse,代表是否已到达集合的末尾。

默认状况下普通对象不可迭代,但若是在其上定义了 Symbol.iterator 属性,则它可变为可迭代对象,以下所示:

collection 对象是可迭代的,由于它定义了 Symbol.iterator 属性。 iterator 使用 Object.keys() 方法获取对象属性名的数组,而后将其赋值给常量values.它还定义了一个计数器变量i,初始值为 0. 当执行迭代器时,它返回一个包含 next() 方法的对象。 每次调用 next() 方法时,它都返回一个 {value, done} 键值对,其中 value 保存集合中的下一个元素,done 保存一个布尔值,表示迭代器是否已达到集合的末尾。

虽然以上代码运行完美,但它本无需如此复杂。 所幸,生成器( generator )函数能够大大简化该过程:

在今生成器中,for...in 循环用于枚举集合,yield 每一个属性的值。 结果与前一个示例彻底相同,但代码量大大减小。

迭代器的缺点是它们不适合表示异步数据源。 ES2018 的补救方案是异步迭代器(asynchronous iterators)和异步可迭代对象(asynchronous iterables)。 异步迭代器与传统迭代器的不一样点在于,它不返回 {value,done} 的形式的普通对象,而是返回一个完成(fulfill) {value,done}promise.异步可迭代对象定义了一个返回异步迭代器的 Symbol.asyncIterator 方法(注意不是 Symbol.iterator)。

举个例子🌰可能更清楚些:

请注意,使用 promises 的迭代器不可能达到相同的结果。 虽然普通的同步迭代器能够异步产生肯定值,但它仍然须要同步肯定“完成(done)”的状态。


一样,你可使用生成器函数简化此过程,以下所示:

一般生成器函数会返回带有next()方法的生成器对象。 当调用 next() 时,它返回一个 {value,done} 键值对,其 value 属性保存了yield 的值。 异步生成器与之相似,只不过它返回的是一个完成了 {value,done} 的promise.

使用 for...of 语句能够轻松迭代可迭代对象,可是 for...of 不能与异步可迭代对象一块儿使用,由于 valuedone 不是同步产生的。 出于这个缘由,ES2018 提供了 for...await...of 语句。 咱们来看一个例子:

在此代码中, for...await...of 语句隐式调用集合对象上的Symbol.asyncIterator方法以获取异步迭代器。 每次循环时,都会调用迭代器的 next() 方法,该方法返回一个 promise. 一旦 promise 完成,就会将结果对象的 value 属性读取到x变量。 循环继续,直到返回对象的 done 属性值为true.

敲黑板!for...await...of 语句仅在异步生成器和异步函数中有效。 违反此规则会报 SyntaxError(语法错误)。


next() 方法可能会返回 rejected promise. 为了优雅地处理被 reject 的 promise,你可使用try...catch语句包裹for...await...of 语句,以下所示:

异步迭代器支持

Node.js:

  • 8.10.0(须要 --harmony_async_iteration flag)

  • 10.0.0(彻底支持)


Promise.prototype.finally

ES2018另外一振奋人心的特效是finally()方法。 以前有几个 JavaScript 库实现了相似的方法,而且它被证明是有用的。这促使 Ecma技术委员会正式将finally()添加到规范中。 使用该方法,开发者可无需理会 promise 命数如何,直接执行这个代码块中的代码。 咱们来看一个简单的例子:

finally() 方法可在操做完成后进行一些扫尾(clean up)工做,不管操做是否成功。 在此代码中,finally() 方法在数据获取处理后直接隐藏了加载 spinner。 不管 promise 完成与否,函数中的注册代码都会执行,开发者没必要在 then()catch() 方法中重复编写逻辑。

使用promise.then(func, func)也可实现与promise.then(func, func) 一样的效果,但你必须在 fulfillment 句柄及 rejection 句柄中重复相同的代码,或者引入一个变量:


then()catch() 相同,finally() 方法老是返回一个 promise,所以你能够连接更多的方法。 通常来讲,咱们会将 finally() 做为最后一环。但某些状况,例如在建立 HTTP 请求时,在 finally() 以后连接另外一个catch(),以处理请求中可能发生的错误是不错的实践。

Promise.prototype.finally 支持

Node.js:

10.0.0(彻底支持)


RegExp 新特性

ES2018 为 RegExp 对象增长了四个新特性,进一步提升了 JavaScript 的字符串处理能力。 这些特性以下:

  • s(dotAll)标志

  • 可命名捕获组

  • Lookbehind断言

  • Unicode 属性转义

s (dotAll) Flag

点(.)是正则表达式模式中的特殊字符,它匹配除换行符以外的任何字符,例如换行符(\n)或回车符(\r)。要匹配包括换行符在内的全部字符,解决方法是使用两个相反短字的字符类,例如[\d\D]. 此字符类告诉正则表达式引擎找到一个数字(\d)或非数字(\D)的字符。 所以,它匹配任意字符:

ES2018 引入了一种模式,其中点可用于实现相同的结果。可使用s标志在每一个正则表达式的基础上激活此模式:

利用标志来选择性使用新特性的好处是向后兼容,保证使用点字符的现有正则表达式模式不受影响。

可命名捕获组

在一些正则表达式模式中,使用数字来引用捕获组可能会形成混淆。 例如,采用正则表达式 /(\d{4})-(\d{2})-(\d{2})/ 匹配日期。 因为美式英语中的日期符号与英式英语不一样,所以很难知道哪一个组指的是日,哪一个组指的是月:

ES2018 引入了使用(?<name>...)语法的命名捕获组。 所以,匹配日期的模式能够用不太模糊的方式编写:

你可使用 \k<name> 语法在模式中再次调用命名捕获组。 例如,要查找句子中连续的重复单词,可使用 /\b(?<dup>\w+)\s+\k<dup>\b/:

要将命名捕获组用于 replace() 方法的替换字符串,你可使用 $<name> 构造。 例如:

后行断言

ES2018 为 JavaScript 带来了后行断言(lookbehind assertion),该断言已在其余语言的正则表达式使用多年。 之前,JavaScript 只支持先行断言(lookahead assertion)。 后行断言用 (?<=...) 表示,使你可以根据模式以前的子字符串匹配模式。 例如,若是你想要在不捕获货币符号的状况下以美圆,英镑或欧元匹配产品的价格,你可使用/(?<=\$|£|€)\d+(\.\d*)?/:

还有一个负向的后行断言,用 (?<!...) 表示。 负向后行断言容许你匹配不跟在某后行断言以后的模式(译者注:差点把我本身都绕晕了😷 举个简单的例子(?<!a)b: 断言 b 前面没有 a,匹配 bb 但不匹配 ab,最终捕获 b)。 例如,模式 /(?<!un)available/ 可在无 “un” 前缀的状况下匹配 available:

Unicode 属性转义

ES2018 提供了一种称为 Unicode 属性转义的新转义序列类型,它在正则表达式中提供对完整 Unicode 的支持。 假设你要匹配字符串中的 Unicode 字符 ㉛. 虽然咱们认为 ㉛ 是一个数字,可是咱们不能用 \d 匹配它,由于它只支持 ASCII [0-9] 字符。此外,Unicode 属性转义也可用于匹配 Unicode 中的任何十进制数:

一样,若是要匹配任意 Unicode 单词(划掉)字母字符,可使用 \p{Alphabetic}:

还有一个否认版本的\p{...},用\P{...},表示:

除了字母和数字以外,还有几个属性能够在 Unicode 属性转义中使用。 你能够在当前规范提案中找到支持的 Unicode 属性列表。

地址:https://tc39.github.io/proposal-regexp-unicode-property-escapes/#sec-static-semantics-unicodematchproperty-p

RegExp 新特性支持

Node.js:

  • 8.3.0(须要 --harmony 运行时 flag)

  • 8.10.0(支持 s(dotAll) 标志和后行断言)

  • 10.0.0(彻底支持)


模板字符串修订

当模板字符串紧跟在表达式以后时,它会被称为标记模板字符串。 当你想要使用函数解析模板字符串时,标记模板会派上用场。 看看这个例子:

上面的代码调用了标记表达式(它是常规函数)并传递模板字符串。 该函数只是修改字符串的动态部分并返回它。

在 ES2018 以前,标记的模板字符串具备与转义序列相关的语法限制。 反斜杠后跟某些字符序列被视为特殊字符:\x 被解析为十六进制转义符,\u 被解析为unicode转义符,\_ 后跟一个数字被解析为八进制转义符。 所以,解释器将诸如 "C:\xxx\uuu""\ubuntu" 之类的字符串视为无效的转义序列,并将抛出SyntaxError.

ES2018 从标记模板中删除了这些限制,它会将无效转义序列表示为 undefined,而不是抛出错误:

请记住,在常规模板字符串中使用非法转义序列仍会报错:

模板字符串修订支持

Node.js:

  • 8.3.0 (须要 --harmony 运行时 flag)

  • 8.10.0(彻底支持)


总结

咱们已经仔细研究了 ES2018 中引入的几个关键特性,包括异步迭代,rest/spread 属性,Promise.prototype.finally以及 RegExp对象的新增特性。 虽然有些浏览器厂商还没有彻底实现其中一些功能,但因为有 Babel 这样的 JavaScript 转换器,咱们仍能够在今天使用它们。

ECMAScript 正在迅速发展,而且每隔一段时间就会引入新功能,欢迎查看完整提案列表👏,了解所有新功能。 有啥功能让你特别兴奋吗?快快和我分享叭~

提案地址:https://github.com/tc39/proposals/blob/master/finished-proposals.md


原文地址:https://css-tricks.com/new-es2018-features-every-javascript-developer-should-know/


好文推荐:

一块儿来燃烧 Bundle 的“卡路里”



“UC国际技术”致力于与你共享高质量的技术文章

欢迎关注咱们的公众号、将文章分享给你的好友

相关文章
相关标签/搜索