1.async/await 异步迭代

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);
  }
}

2.Promise.finally()

ES6为我们带来了Promise,但是它的结果要么成功then要么失败catch,使得我们的一些逻辑,如执行状态修改,结束回调都得在两边写一遍。

选择有了finally(),逻辑只可以放在一个地方了,这有点像以前jQuery ajaxcomplete

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

finally()没有参数传入。

3.Rest/Spread 属性

ES2015引入了Rest参数和扩展运算符。当时三个点...仅用于数组。

Rest参数语法允许我们将一个剩余参数表示为一个数组。

function foo(a, b, ...rest) {
  // a = 1
  // b = 2
  // rest = [3, 4, 5]
}
foo(1, 2, 3, 4, 5);

展开操作符则是将数组转换成可传递给函数的单独参数。

const nums = [1, 2, 3, 4, 5];
Math.max(...nums));
// 5

现在对象也可以使用它们了。

function foo({ a, b, ...rest }) {
  // a = 1
  // b = 2
  // rest = { c: 3, d: 4, e: 5 }
foo({
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
});
const object = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
};
const { a, ...rest } = object;
// a = 1
// b = 2
// rest = { c: 3, d: 4, e: 5 }

跟数组一样,Rest参数只能在声明的结尾处使用。

4.正则表达式 s(dotAll模式)标记

在正则中,.可以匹配任意字符,除了换行符。

/hello.es9/.test('hello\nes9');
// false

ES2018引入了dotAll模式,通过使用标记s选项,.就可以匹配换行符。

/hello.es9/s.test('hello\nes9');
// true

5.正则表达式 Unicode 转义

目前在正则中,可以通过字符集的名称来匹配字符。如s代表空白

/^\s+$/u.test(' ');
// true

在ES2018添加了Unicode属性转义,形式为\p{...}\P{...},在正则表达式中使用标记u(unicode) 选项。

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

6.lookbehind 反向断言

目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但是断言没有包含在整个匹配字段中。

如匹配字符串“10 hours”中紧跟着是”hours”的数字:

const reg = /\d+(?= hours)/u;
const matched = reg.exec('10 hours');
matched[0];
// 42

匹配字符串“10 minutes”中紧跟着不是”hours”的数字:

const reg = /\d+(?! hours)/u;
const matched = reg.exec('10 minutes');
matched[0];
// 42

ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind)。

匹配字符串“hours10”中”hours”后面的数字:

const reg = /(?<=hours)\d+/u;
const matched = reg.exec('hours10');
matched[0];
// 10

匹配字符串“minutes10”中数字前面不是“hours”:

const reg = /(?<!hours)\d+/u;
const matched = reg.exec('minutes10');
matched[0];
// 10

7.Named capture groups 正则表达式命名捕获组

目前,正则表达式中小括号匹配的分组是通过索引编号的:

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

代码可读性较差,并且改变正则表达式的结构有可能改变匹配对象的索引。

ES2018允许命名捕获组使用符号?<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日

8.非转义序列的模板字符串

ES2018 移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。

之前,\u开始一个 unicode 转义,\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111