ES9 - 新特性



在这里,我们将学习 ES9 中的新特性。让我们从了解异步生成器开始。

异步生成器和迭代

异步生成器可以通过使用 **async** 关键字使其成为异步的。定义异步生成器的 **语法** 如下所示:

async function* generator_name() {
   //statements
}

示例

以下示例显示了一个异步生成器,它在每次调用生成器的 **next()** 方法时返回 Promise。

<script>
   async function* load(){
      yield await Promise.resolve(1);
      yield await Promise.resolve(2);
      yield await Promise.resolve(3);
   }
   
   let l = load();
   l.next().then(r=>console.log(r))
   l.next().then(r=>console.log(r))
   l.next().then(r=>console.log(r))
   l.next().then(r=>console.log(r))
</script>

以上代码的输出如下所示:

{value: 1, done: false}
{value: 2, done: false}
{value: 3, done: false}
{value: undefined, done: true}

for await of 循环

异步可迭代对象不能使用传统的 **for..of 循环** 语法进行迭代,因为它们返回 Promise。ES9 引入了 **for await of 循环** 来支持 **异步迭代**。

使用 **for await of 循环** 的语法如下所示,其中:

  • 在每次迭代中,一个不同属性的值被赋值给 **变量**,并且变量可以用 const、let 或 var 声明。

  • **iterable** - 要迭代其可迭代属性的对象。
for await (variable of iterable) {
   statement
}

示例

以下示例演示了如何使用 for await of 循环迭代异步生成器。

<script>
   async function* load(){
      yield await Promise.resolve(1);
      yield await Promise.resolve(2);
      yield await Promise.resolve(3);
   }

   async function test(){
      for await (const val of load()){
         console.log(val)
      }
   }
   test();
   console.log('end of script')
</script>

以上代码的输出将如下所示:

end of script
1
2
3

示例

以下示例使用 for await of 循环迭代数组。

<script>
   async function fntest(){
      for await (const val of [10,20,30,40]){
         console.log(val)
      }
   }
   fntest();
   console.log('end of script')
</script>

以上代码的输出如下所示:

end of script
10
20
30
40

剩余/扩展属性

ES9 支持在对象中使用剩余和扩展运算符。

示例:对象和剩余运算符

以下示例演示了如何在对象中使用剩余运算符。student 的 age 属性的值被复制到 age 变量中,而其余属性的值使用剩余语法 `...` 复制到 other 变量中。

<script>
   const student = {
      age:10,
      height:5,
      weight:50
   }
   const {age,...other} = student;
   console.log(age)
   console.log(other)
</script>

以上代码的输出将如下所示:

10
{height: 5, weight: 50}

示例:对象和扩展运算符

扩展运算符可以用来组合多个对象或克隆对象。这在以下示例中显示:

<script>
   //spread operator
   const obj1 = {a:10,b:20}
   const obj2={c:30}
   //clone obj1
   const clone_obj={...obj1}
   //combine obj1 and obj2
   const obj3 = {...obj1,...obj2}
   console.log(clone_obj)
   console.log(obj3)
</script>

以上代码的输出将如下所示:

{a: 10, b: 20}
{a: 10, b: 20, c: 30}

Promise: finally()

无论 Promise 的结果如何,**finally()** 都会在 Promise 完成时执行。此函数返回一个 Promise。它可以用于避免在 Promise 的 **then()** 和 **catch()** 处理程序中重复代码。

语法

以下是 **finally()** 函数的语法。

promise.finally(function() {
});
promise.finally(()=> {
});

示例

以下示例声明了一个异步函数,该函数在延迟 3 秒后返回正数的平方。如果传递负数,则该函数会抛出错误。无论 Promise 是否被拒绝或解决,finally 块中的语句都会在任何情况下执行。

<script>
   let asyncSquareFn = function(n1){
      return new Promise((resolve,reject)=>{
         setTimeout(()=>{
            if(n1>=0){
               resolve(n1*n1)
            }
            else reject('NOT_POSITIVE_NO')
         },3000)
      })
   }
   console.log('Start')

   asyncSquareFn(10)//modify to add -10
   .then(result=>{
      console.log("result is",result)
   }).catch(error=>console.log(error))
   .finally(() =>{
      console.log("inside finally")
      console.log("executes all the time")
   })

   console.log("End");
</script>

以上代码的输出将如下所示

Start
End
//after 3 seconds
result is 100
inside finally
executes all the time

模板字面量修订

从 ES7 开始,标记模板符合以下转义序列的规则:

  • Unicode 转义序列使用 **"\u"** 表示,例如 **\u2764\uFE0F**

  • Unicode 代码点转义序列使用 **"\u{}"** 表示,例如 **\u{2F}**

  • 十六进制转义序列使用 **"\x"** 表示,例如 **\xA8**

  • 八进制字面量转义序列使用 "" 并后跟一个或多个数字表示,例如 **\125**

在 ES2016 及更早版本中,如果带标记函数使用无效转义序列,则会抛出语法错误,如下所示:

//tagged function with an invalid unicode sequence
myTagFn`\unicode1`
// SyntaxError: malformed Unicode character escape sequence

但是,与早期版本不同,ES9 将无效的 Unicode 序列解析为 undefined,并且不会抛出错误。这在以下示例中显示:

<script>
   function myTagFn(str) {
      return { "parsed": str[0] }
   }
   let result1 =myTagFn`\unicode1` //invalid unicode character
   console.log(result1)
   let result2 =myTagFn`\u2764\uFE0F`//valid unicode
   console.log(result2)
</script>

以上代码的输出将如下所示:

{parsed: undefined}
{parsed: "❤️"}

原始字符串

ES9 引入了一个特殊的属性 **raw**,它在传递给标记函数的第一个参数上可用。此属性允许您访问原始字符串,就像它们输入时一样,无需处理转义序列。

示例

<script>
   function myTagFn(str) {
      return { "Parsed": str[0], "Raw": str.raw[0] }
   }
   let result1 =myTagFn`\unicode`
   console.log(result1)

   let result2 =myTagFn`\u2764\uFE0F`
   console.log(result2)
</script>

以上代码的输出如下所示:

{Parsed: undefined, Raw: "\unicode"}
{Parsed: "❤️", Raw: "\u2764\uFE0F"}

正则表达式特性

在正则表达式中,点运算符或句点用于匹配单个字符。**. 点运算符** 跳过换行符,如 **\n、\r**,如下例所示:

console.log(/Tutorials.Point/.test('Tutorials_Point')); //true
console.log(/Tutorials.Point/.test('Tutorials\nPoint')); //false
console.log(/Tutorials.Point/.test('Tutorials\rPoint')); //false

正则表达式模式表示为 / **regular_expression /。**test() 方法接受一个字符串参数并搜索正则表达式模式。在上面的示例中,**test() 方法** 搜索以 Tutorials 开头、后跟任何单个字符并以 Point 结尾的模式。如果我们在 Tutorials 和 Point 之间的输入字符串中使用 **\n** 或 **\r**,则 test() 方法将返回 false。

true
false
false

ES9 引入了一个新的标志 - **DotAllFlag (\s)**,它可以与 Regex 一起使用以匹配行终止符和表情符号。这在以下示例中显示:

console.log(/Tutorials.Point/s.test('Tutorials\nPoint'));
console.log(/Tutorials.Point/s.test('Tutorials\rPoint'));

以上代码的输出将如下所示:

true
true

命名捕获组

在 ES9 之前,捕获组是通过索引访问的。ES9 允许我们为捕获组分配名称。语法如下所示:

(?<Name1>pattern1)

示例

const birthDatePattern = /(?<myYear>[0-9]{4})-(?<myMonth>[0-9]{2})/;
const birthDate = birthDatePattern.exec('1999-04');
console.log(birthDate.groups.myYear);
console.log(birthDate.groups.myMonth);

以上代码的输出如下所示:

1999
04
广告