ECMAScript 2018(ES9)新特性

ECMAScript 2018(ES9)新特性:

  • 异步迭代: async await
  • Promise 方法:: finally() 。无论结果是 Resolved 或者是 Rejected 都会执行。
  • Rest Spread 操作符和对象构建。
  • 正则表达式:命名捕获组、反向断言、Unicode 属性转义。
  • 模板文字和带标签的模板文字。


异步迭代:async、await

在异步/等待过程中的某个时刻,您将尝试在同步循环内调用异步函数。

async function process(array) {
  for (let i of array) {
    await doSomething(i);
  }
}
// 它不会起作用
async function process(array) {
  array.forEach(async i => {
    await doSomething(i);
  });
}
// 它不会起作用

循环本身保持同步,并且总是在它们的内部异步操作之前完成。ES2018 引入了异步迭代器,它与常规迭代器一样,只是方法返回一个 Promise。因此,关键字可以与循环一起使用以串行运行异步操作。

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


Promise.finally()

在之前的 Promise 的调用链要么调用成功返回 .then() 方法,要么调用失败返回 .catch() 方法。在某些情况下,你想要在无论是成功还是失败,都运行同样的代码。例如实现清除、删除对话、关闭数据连接等操作。 Promise.finally() 允许你指定最终的逻辑。这为指定执行完 Promise 后,无论结果是 Resolved 或者是 Rejected 都需要执行的代码提供了一种方式。

Promise.finally() 法接受一个回调函数作为参数,由于无法知道 Promise 实例的最终状态,所以 finally() 的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。

server.listen(0)
.then(function (){})
.catch(function (){})
.finally(server.stop);
function doSomething() {
  doSomething1()
  .then(doSomething2)
  .then(doSomething3)
  .catch(err => {
    console.log(err);
  })
  .finally(() => {
    // finish here!
  });
}


Rest/Spread操作符和对象构建

Rest和Spread的操作符都是 ... ,只不过使用的场景和目的不一样。Rest(展开)主要用在对象的解构,目前只支持对象的解构和不确定的参数描述。Spread(扩展)主要用在字面量对象的构建上。

数组

ES2015 为数组引入了 Rest(展开)参数和 Spread(扩展)运算符。

restParam(1, 2, 3, 4, 5);
function restParam(p1, p2, ...p3) {  
    // p1 = 1
    // p2 = 2
    // p3 = [3, 4, 5]
}

Spread 运算符以相反的方式工作,并将数组转换为可以传递给函数的单独参数。例如,给定任意数量的参数,返回最高值:Math.max()

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100


对象

ES2018 为对象解构提供了和数组一样的 Rest(展开)参数和 Spread(扩展)运算符。

const myObject = {
  a: 1,
  b: 2,
  c: 3
};

const { a, ...x } = myObject;
// a = 1
// x = { b: 2, c: 3 }

或者您可以使用它将值传递给函数。

restParam({
  a: 1,
  b: 2,
  c: 3
});

function restParam({ a, ...x }) {
  // a = 1
  // x = { b: 2, c: 3 }
}

跟数组一样,Rest参数只能在声明的结尾处使用。此外,它只适用于每个对象的顶层,如果对象中潜逃对象则无法适用。扩展运算符可以在其他对象内使用。

const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, z: 26 };
// obj2 is { a: 1, b: 2, c: 3, z: 26 }

您可以使用 Spread 运算符来克隆对象,但请注意,您只能获得浅拷贝。如果属性包含另一个对象,则克隆将引用同一对象。 obj2 ={...obj1};


正则表达式

命名捕获组

JavaScript 正则表达式可以返回匹配对象-类似于数组的值,包含匹配的字符串。例如,要以 YYYY-MM-DD 格式解析日期:

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

它很难阅读,并且更改正则表达式也可能会更改匹配对象索引。

ES2018 允许在开始捕获括号后立即使用符号命名组。例如: ?

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

任何未匹配的命名组都将其属性设置为 undefined。

命名捕获也可用于方法中。例如,将日期转换为美国 MM-DD-YYYY 格式: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>');


反向断言

JavaScript 目前支持正则表达式中的 前瞻断言 。这意味着必须进行匹配但不捕获任何内容,并且断言不包含在整个匹配的字符串中。例如,要从任何价格中捕获货币符号:

const
  reLookahead = /\D(?=\d+)/,
  match       = reLookahead.exec('$123.89');

console.log( match[0] ); // $

ES2018 引入以相同方式工作但是匹配前面的 反向断言 。这样我就可以忽略货币符号,单纯的捕获价格的数字:

const
  reLookbehind = /(?<=\D)\d+/, match = reLookbehind.exec('$123.89'); console.log( match[0] ); // 123.89

以上是 肯定反向断言 ,非数字 \D 必须存在。同样的,还存在 否定反向断言 ,表示一个值必须不存在。例如:

const
  reLookbehindNeg = /(?// null<、small>


dotAll 模式

正则表达式中 . (点)匹配除回车外的任何单字符,标记 s 改变这种行为,允许 行终止符 的出现,例如:

/hello.world/s.test('hello\nworld'); // true


Unicode 属性转义

到目前为止,还无法在正则表达式中本机访问 Unicode 字符属性。ES2018 在设置了 u (Unicode)标志的正则表达式中,以 \p{…} \p{…} 的形式,添加 Unicode 属性转义。

const reGreekSymbol = /\p{Script=Greek}/u;
reGreekSymbol.test('π'); // true


模板文字调整

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

ES2018 删除了与模板文字中的转义序列相关的所有语法限制。