ES2018 里包括哪些新特性?
栏目: JavaScript · 发布时间: 5年前
内容简介:我们一路奋战,不是为了改变世界,而是为了不让世界改变我们。——《熔炉》JavaScript 标准 ECMAScript 的推进是非常活跃的了,自 ES2015 开始,标准制定委员会 TC39 决定每年为一个周期,推出新版本标准,这无疑为 JavaScript 语言本身注入了强大的活力。
我们一路奋战,不是为了改变世界,而是为了不让世界改变我们。
——《熔炉》
JavaScript 标准 ECMAScript 的推进是非常活跃的了,自 ES2015 开始,标准制定委员会 TC39 决定每年为一个周期,推出新版本标准,这无疑为 JavaScript 语言本身注入了强大的活力。
一个新特性在写入标准前,一般需要经过 5 个阶段:
- Stage 0 - Strawman(展示阶段):最初提交的想法。
- Stage 1 - Proposal(征求意见阶段):一个正式提案文档,且至少有一位 TC39 的成员支持,其中包括 API 示例。
- Stage 2 - Draft(草案阶段):特性规范的初始版本,具有两个实验性实现。
- Stage 3 - Candidate(候选人阶段):对提案规范进行评审,并从第三方厂商收集反馈。
- Stage 4 - Finished(定案阶段):提案已经准备好包含在 ECMAScript 中,但是在浏览器和 Node.js 中可能需要一些时间实现。
一个提案只要能进入 Stage 2,就差不多肯定会包括在以后的正式标准里面。ECMAScript 当前的所有提案,可以在 TC39 的官方网站 GitHub.com/tc39/ecma26… 查看。
在正式讲解 ES2018 前,先回顾一下 ES2016 和 ES2017 中引入的新特性。
ES2016
-
数组的
includes
方法 -
指数运算符
**
,比如a ** b
和Math.pow(a, b)
的效果一样
ES2017
- 异步函数
-
Object.values
,返回由对象属性值组成的数组 -
Object.entries
,返回由一个个由[key, vlaue]
组成的数组 -
Object.getOwnPropertyDescriptors
,返回一个对象,包含目标对象身上所有的属性描述符(.value
、.writable
、.get
、.set
、.configurable
、.enumerable
) -
字符串的
padStart
和padEnd
方法 - 定义对象、声明数组以及在函数的参数列表中,可使用尾逗号。
-
用来读取和写入到共享内存的
SharedArrayBuffer
和Atomics
。
好了,现在来看下 ES2018 增加的新特性吧 :heart_eyes:。
ES2018
异步迭代
如果一个异步函数中包含一个循环,循环里的每一次迭代是发起一个异步请求。那么怎么保证在本次迭代的请求完成后,再继续进入下一次迭代呢?
你可能想过像下面这样做:
async function process(array) { for (let i of array) { await fetch(`https://jsonplaceholder.typicode.com/todos/${i}`) } } 复制代码
但是不行的。
那么这样呢?也不行。☹️
// 这样也不行 async function process(array) { array.forEach(async i => { await fetch(`https://jsonplaceholder.typicode.com/todos/${i}`) }) } 复制代码
因为循环本身是同步的,不会等里面的异步请求完成后再进入下一次迭代的。
ES2018 引入了异步迭代器,与普通迭代器不同的是,异步迭代器的 next
方法返回的是一个 Promise。因此, await
关键字用在 for...of
之前,可以完成一系列按特定顺序发生的 请求->等待->完成->进入下一次迭代
的操作流程。例如:
// 这样 OK :smiley: async function process(array) { for await (let i of array) { await fetch(`https://jsonplaceholder.typicode.com/todos/${i}`) } } 复制代码
Promise.finally()
.finally()
方法总是会被执行,无论 Promise 最终状态是 resolve 了,还是 reject 了。类似于 try...catch...finally
里的 finally
块的作用。
async function process(array) { fetch('https://jsonplaceholder.typicode.com/todos/1') .then(response => response.json()) .then(json => console.log(json)) .catch(error => { console.log(error) }) .finally(() => { // 这里的代码总是会被执行 // 无论 Promise 最终状态是 resolve 了,还是 reject 了 }) } 复制代码
剩余/扩展属性
ES2015 引入了三个点( ...
)运算符,这个运算符既能用来收集函数的剩余参数,也可以用来扩展数组。
首先来看作为剩余参数运算符的例子:
doSomething(1, 2, 3, 4, 5) function doSomething(p1, p2, ...p3) { // p1 的值为 1 // p2 的值为 2 // p3 是一个数组,值为 [3, 4, 5] } 复制代码
再来看用做扩展运算符的例子:
const values = [99, 100, -1, 48, 16]; console.log( Math.max(...values) ); // 100 复制代码
我们可以看到,扩展运算符与剩余参数运算符的用法是互为反向的。
ES2015 引入的 ...
运算符,实际上仅适应在对数组的操作上,ES2018 对其进行了增强,将扩展和收集参数的能力扩大到了对象。使得 ...
运算符也可以用来收集对象的“剩余属性”。
一个基础的例子:
const obj = { a: 1, b: 2, c: 3 } const { a, ...x } = obj // a 等于 1 // x 的值为 { b: 2, c: 3 } 复制代码
这里的 a
对应的是 obj
的属性 a
; x
则是由 obj
中除 a
属性以外的其他属性组成的对象。
我们借用这个例子里的 obj
,再来看一个例子:
doSomething(obj) function doSomething({ a, ...x }) { // a = 1 // x = { b: 2, c: 3 } } 复制代码
doSomething
函数接收一个对象参数,调用后,将这个对象拆分成变量 a
和 x
。
我们也可以使用 ...
运算符的扩展功能,将一个对象“扩展”进另一个对象。
const obj1 = { a: 1, b: 2, c: 3 }; const obj2 = { ...obj1, z: 26 }; // obj2 值变成了 { a: 1, b: 2, c: 3, z: 26 } 复制代码
使用这个特性,我们还可以实现对象的浅克隆: obj2 = { ...obj1 }
。
正则表达式的命名捕获组
在 ES2018 之前,我们如果要匹配类似 '2018-04-30'
这样的字符串格式,可能会这样做。
const reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/, match = reDate.exec('2018-04-30'), year = match[1], // 2018 month = match[2], // 04 day = match[3]; // 30 复制代码
我们从最终的匹配结果 match
身上,使用索引值,找到对应捕获组匹配的内容。但带来的一个问题是,之后如果匹配格式发生改变,那么 match
对应索引值下内容的含义就不一样了。这样我们势必会修改代码,来提供正确的逻辑。
而 ES2018 允许我们为捕获组命名,命名方式是在 (
前面使用符号 ?<name>
标识。
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/, match = reDate.exec('2018-04-30'), year = match.groups.year, // 2018 month = match.groups.month, // 04 day = match.groups.day; // 30 复制代码
所有的命名组的匹配结果,可以在结果对象的 groups
属性中获得。没有命中的捕获组,取值时得到 undefined
。
除此之外,命名捕获组还可以在字符串的 replace
方法中使用:
const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/, d = '2018-04-30', usDate = d.replace(reDate, '$<month>-$<day>-$<year>'); 复制代码
这里我们将 '2019-05-18'
格式化为 '05-18-2019'
。在替换字符串中,使用 $<name>
的形式加入该命名捕获组匹配到的内容。
正则表达式的后行断言
JavaScript 正则表达式天然支持前行断言。
所谓前言断行,是指 x
只有在 y
前面时才匹配,书写形式如 /x(?=y)/
。比如,下面的例子里,我们只匹配数字之前的美元符号:
const reLookahead = /\$(?=\d+)/, match = reLookahead.exec('$123'); console.log( match[0] ); // $ 复制代码
对应地,所谓后行断断言,是指 x
只有在 y
后面时才匹配,书写形式如 /(?<=y)x/
。同样上面的例子,我们只匹配美元符号之后的数字:
const reLookahead = /(?<=\$)\d+/, match = reLookahead.exec('$123'); console.log( match[0] ); // 123 复制代码
正则表达式的 s 修饰符:dotAll 模式
正则表达式的 .
匹配任意字符,但有两个例外:
-
不能识别码点大于
0xFFFF
的 Unicode 字符,这些字符每个占用 4 个字节。 -
终止符:包括换行符(
\n
)、回车符(\r
)在内的字符。
针对第一条限制,我们可以使用 u
修饰符解决;针对第二条限制,我们则可以使用 s
修饰符。
// . 不匹配 \n,所以正则表达式返回 false /hello.world/.test('hello\nworld') // false // 这样就可以了 /hello.world/s.test('hello\nworld') // true 复制代码
正则表达式的 Unicode 属性类
ES2018 引入了一种新的类的写法 \p{...}
和 \P{...}
,允许正则表达式匹配符合 Unicode 某种属性的所有字符。
const regexGreekSymbol = /\p{Script=Greek}/u; regexGreekSymbol.test('π') // true 复制代码
上面代码中, \p{Script=Greek}
指定匹配一个希腊文字母,所以匹配 π
成功。
\P{…}
是 \p{…}
的反向匹配,即匹配不满足条件的字符。
注意,这两种类只对 Unicode 有效,所以使用的时候一定要加上 u
修饰符。如果不加 u
修饰符,就会报错。
模板字符串微调
在 JavaScipt 中,字符串中的 \
表示一个转义字符,它提供了 5 种表达字符的方式:
'\z' === 'z' // true(z 无特殊含义,直接输出) '\172' === 'z' // true(字符的八进制表示) '\x7A' === 'z' // true(字符的十六进制表示) '\u007A' === 'z' // true(字符的十六进制表示) '\u{7A}' === 'z' // true(字符的十六进制表示,支持任意 Unicode 字符码点) 复制代码
这就带来了一个问题,如果字符串中包含 \unicode
或 \xerxes
的话,就会报错,因为会被认为是无效的字符转义。
为了解决这个问题,ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符转义,就返回 undefined
,而不是报错,并且可以从 raw
属性上面可以得到原始字符串。
function tag(strs) { // strs[0] 等于 undefined // strs.raw[0] 等于 "\\unicode and \\u{55}"; } tag`\unicode and \u{55}` 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 包括外部php文件
- Php包括恼人的上边距
- jQuery submit()不包括提交的按钮
- 记录一次搜狐面试(包括笔试题)
- 数据中台到底包括什么内容?一文详解架构设计与组成
- 人工智能技术主要包括五大类型,你知道几个?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。