每个 JavaScript 开发者都该了解的 ES2018 新特性
栏目: JavaScript · 发布时间: 5年前
内容简介:原文作者:Faraz Kelhini译者:UC 国际研发 Jothy写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。
原文作者:Faraz Kelhini
译者:UC 国际研发 Jothy
写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。
编者按:曾几何时,年少的我捧着阮一峰老师的《ES6 标准入门》,感叹 JS 变迁实在太快,好怕学不动了。直至写了几年 ES6 的今日,回头看方知:不要为了学 ES X 而学 ES X,无论 ES 几其实都是语法糖,是辅助角色,重点是想清楚它能为我们的开发带来什么好处,而不是本末倒置。今天介绍的 ES2018 新特性还是有蛮多亮点的,一起来看看吧。
ECMAScript 标准的第九版,官宣为 ECMAScript 2018(或简称 ES2018),已于 2018 年 6 月发布。从 ES2016 开始,每隔一年就会发布一版 ECMAScript 规范的新版本,并添加不多于主版本的功能。 最新的这个版本延续了每年发布的周期,新增了四个新的 RegExp
特性,rest/spread 属性,异步迭代和 Promise.prototype.finally
。 此外,它还从标记模板中删除了转义序列的语法限制。
我们将在后面的小节中详细解释这些新变化 :wink:。
rest/spread 属性
回顾 ES2015,最有趣的功能当数 spread 运算符。 该运算符极大简化了数组的复制及合并。你可以使用它替换 concat()
或 slice()
方法:
在必须将数组的各个项分别作为参数传入函数的情况下,扩展运算符也派得上用场。 例如:
通过向对象文法添加 spread 属性,ES2018 进一步扩展了此语法。 使用 spread 属性,你可以将对象自身的可枚举属性复制到新对象上。举个例子 :chestnut::
在此代码中, ...
运算符用于检索 obj1 的属性并将它们分配给 obj2。 在 ES2018 之前,这么做会报错的。 如果出现多个属性同名的情况,将会取最后一个值:
Spread 属性还提供了一种合并两个或多个对象的新方法,可以替代 Object.assign()
方法使用:
在此代码中, Object.assign()
方法会执行其继承的 setter 属性,而 spread 属性则完全忽略了这一步。
切记!spread 属性只复制可枚举属性。 在下面的例子中, type
属性不会出现在复制出的对象中,因为其 enumerable
属性为 false
:
继承属性即使是可枚举的,也会被忽略:
在这段代码中, car2
继承了 car
的 color
属性。 由于 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...in
和 while
等语句,以及 map()
, filter()
和 forEach()
等方法。 为了方便 程序员 一个个地处理集合元素,ES2015 引入了迭代器接口。
如果对象具有 Symbol.iterator
属性,则表示它是可迭代的。 在 ES2015 中,字符串和集合对象(如 Set
, Map
和 Array
)带有 Symbol.iterator
属性,因此是可迭代的。 以下代码说明了如何每次访问一个可迭代元素:point_up::
Symbol.iterator
是个广为人知的符号,用于表示返回迭代器的函数。与迭代器交互主要使用 next()
方法。此方法返回一个具有 value
和 done
两个属性的对象。 value
属性包含集合中下一个元素的值。 done
属性包含 true
或 false
,表明是否已到达集合的末尾。
默认情况下普通对象不可迭代,但如果在其上定义了 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
)。
举个例子:chestnut:可能更清楚些:
请注意,使用 promises 的迭代器不可能达到相同的结果。 虽然普通的同步迭代器可以异步产生确定值,但它仍然需要同步确定“完成(done)”的状态。
同样,你可以使用生成器函数简化此过程,如下所示:
通常生成器函数会返回带有 next()
方法的生成器对象。 当调用 next()
时,它返回一个 {value,done}
键值对,其 value
属性保存了yield 的值。 异步生成器与之类似,只不过它返回的是一个完成了 {value,done}
的promise.
使用 for...of
语句可以轻松迭代可迭代对象,但是 for...of
不能与异步可迭代对象一起使用,因为 value
和 done
不是同步产生的。 出于这个原因,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*)?/
:
还有一个负向的后行断言,用 (?<!...)
表示。 负向后行断言允许你匹配不跟在某后行断言之后的模式(译者注:差点把我自己都绕晕了:mask: 举个简单的例子 (?<!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 正在迅速发展,并且每隔一段时间就会引入新功能,欢迎查看完整提案列表:clap:,了解全部新功能。 有啥功能让你特别兴奋吗?快快和我分享叭~
提案地址:https://github.com/tc39/proposals/blob/master/finished-proposals.md
原文地址:https://css-tricks.com/new-es2018-features-every-javascript-developer-should-know/
好文推荐:
“UC国际技术”致力于与你共享高质量的技术文章
欢迎关注我们的公众号、将文章分享给你的好友
以上所述就是小编给大家介绍的《每个 JavaScript 开发者都该了解的 ES2018 新特性》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Chrome 72 开发者工具新特性
- 每个开发者都应该了解的一些C++特性
- 开发者所需要知道的 iOS 11 SDK 新特性
- OpenCenter3.0开发者预览版发布,开发者不能错过的新特性!
- Cocos-BCX链上游戏开发者:关注所在链特性,结合团队优势开发游戏
- 让开发者专注于应用开发,OpenCenter 3.0 开发者预览版发布
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。