持续学习之ES9特性概述

386 阅读2分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

ECMAScript 2018(E9)Features

CMAScript 2018 或 ES9 于 2018 年六月底由 TC39 正式发布。可以在这里浏览完整的版本。

非转义序列的模板字符串

移除了在带标签的模板字符串中转义序列的语法限制
之前,\u开始一个unicode转义,\x开始一个十六进制转义,``后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111

正则表达式Unicode转义

之前, 可以通过字符集的名称来匹配字符, 即s代表空白
/^\s+$/u.test(' ') // true
在es9,添加了Unicode属性转义,形式为\p{...}\P{...},在正则表达式使用标记u

/^\p{White_Space}+$/u.test(' ') // 空格
// true
/^\p{Script=Greek}+$/u.test('μετά') // 希腊字母
// true
/^\p{Script=Latin}+$/u.test('Grüße') // 匹配拉丁字母

正则表达式s标记

之前.可以匹配任意字符,除了换行符
es9后,可以通过标记s,这样.就可以匹配换行符
/hello.es9/s.test('hello\nes9') //true

RegExp named capture groups(正则表达式命名捕获组)

捕获分组和非捕获分组

()表示捕获分组,()会把每个分组里的匹配的值保存起来(存储在内存中),捕获的子序列稍后可以通过 Back 引用(反向引用) 在表达式中使用
(?)开头的就是非捕获分组,不会将匹配的值保存起来,也不能后面引用,其中?:、?=、?<=等都是非捕获元,使用这些非捕获元的分组为非捕获分组
捕获分组都是通过索引编号的, 这样代码可读性差

const reg = /(\d{4})-(\d{2})-(\d{2})/u;
const matched = reg.exec('2018-12-31');
matched[0];  // 2018-12-12
matched[1];  // 2018
matched[2];  // 12
matched[3];  // 31
命名捕获组

es9中,允许命名捕获组使用符号?<name>, 小括号中匹配内容的名称放在groups里,提高了代码的可读性

const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
const matched = reg.exec('2018-12-31')
matched.groups.year;  // 2018
matched.groups.month;  // 12
matched.groups.day;  // 31

命名捕获组也可以使用在replace()方法中
例如将日期转换为“年月日”格式:

const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2018-12-31'.replace(reg, '$<year>年$<month>月$<day>日');
// 2018年12月31日

正则表达式反向断言(lookbehind)

正向先行断言:?=

形式: (?=pattern)
exp1(?=exp2): 查找后面为exp2的exp1

const reg = /runoob(?=[\d+])/g //匹配数字前面的runoob
123456runoob123runoobbdhh // 只会匹配第一个runoob
23runoob456runoob789 //两个都好匹配到

负向先行断言: ?!

形式: (?!pattern)
exp1(?!exp2): 查找后面不为exp2的exp1

const reg = /runboo(?![0-9]+)/g // 匹配后面不是数字的runboo
1233runboo-google12runoob1233 // 匹配第一个

正向后行断言(es9): ?<=

形式: (?<=pattern)
(?<=exp2)exp1: 查找前面是exp2的exp1

const reg = /(?<=[0-9]+)runboo/g //匹配前面是数字的runboo
1234google123runboo456 // 匹配runboo
123googlerunboo123runboo456 // 匹配第二个

负向后行断言(es9):?<!

形式: (?<!pattern)
(?<!exp2)exp1: 查找前面不是exp2的exp1

const reg = /(?<=[0-9]+)runboo/g //匹配前面不是数字的runboo
1234google123runboo456 // 不能匹配到runboo
123googlerunboo123runboo456 // 匹配第一个

扩展运算符(...)

之前...只能用于数组,es9后,也可以用于对象了,用法和数组一样

Promise.finally()

没有参数
之前的Promise的结果要么是成功then要么是失败catch,使得有些公共逻辑代码必须在两个回调函数里面写(例如:执行状态修改, 删除对话等)
然后就有了finally(),逻辑只放在一个地方,无论Promise运行成功还是失败,都会运行

new Promise((reslove, reject) => {
  // ...
}).then((res) => {
  // reslove
}).catch((err) => {
  // reject
}).finally(() => {
  // complete
});

异步迭代

es8中,可以使用async/await在同步的写法中执行异步函数,但是在循环中,循环依然保持同步,即在内部异步函数调用之前循环已经全部完成

async function foo(array) {
  for (let i of array) {
    await doSomething(i);
  }
}

es9引入了异步迭代器(asynchronous iterators),使await可以和for...of循环一起使用

async function foo (array) {
    for await (let i of array) {
        doSomething(i)
    }
}