您现在的位置是:网站首页> 编程资料编程资料

2022发布ECMAScript新特性盘点_javascript技巧_

2023-05-24 373人已围观

简介 2022发布ECMAScript新特性盘点_javascript技巧_

引言

2022 年 6 月 22 日,第 123 届 ECMA 大会批准了 ECMAScript 2022 语言规范,这意味着它现在正式成为标准。下面就来看看 ECMAScript 2022 新增了哪些特性!

总览:

  • Top-level Await
  • Object.hasOwn()
  • at()
  • error.cause
  • 正则表达式匹配索引
  • 类的实例成员

1. Top-level Await

在ES2017中,引入了 async 函数和 await 关键字,以简化 Promise 的使用,但是 await 关键字只能在 async 函数内部使用。尝试在异步函数之外使用 await 就会报错:

SyntaxError - SyntaxError: await is only valid in async function。

顶层 await 允许我们在 async 函数外面使用 await 关键字。它允许模块充当大型异步函数,通过顶层 await,这些 ECMAScript 模块可以等待资源加载。这样其他导入这些模块的模块在执行代码之前要等待资源加载完再去执行。

由于 await 仅在 async 函数中可用,因此模块可以通过将代码包装在 async 函数中来在代码中包含 await

 // a.js import fetch from "node-fetch"; let users; export const fetchUsers = async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); } fetchUsers(); export { users }; // usingAwait.js import {users} from './a.js'; console.log('users: ', users); console.log('usingAwait module'); 

我们还可以立即调用顶层async函数(IIAFE):

import fetch from "node-fetch"; (async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); })(); export { users }; 

这样会有一个缺点,直接导入的 usersundefined,需要在异步执行完成之后才能访问它:

// usingAwait.js import {users} from './a.js'; console.log('users:', users); // undefined setTimeout(() => { console.log('users:', users); }, 100); console.log('usingAwait module'); 

当然,这种方法并不安全,因为如果异步函数执行花费的时间超过100毫秒, 它就不会起作用了,users 仍然是 undefined

另一个方法是导出一个 promise,让导入模块知道数据已经准备好了:

//a.js import fetch from "node-fetch"; export default (async () => { const resp = await fetch('https://jsonplaceholder.typicode.com/users'); users = resp.json(); })(); export { users }; //usingAwait.js import promise, {users} from './a.js'; promise.then(() => { console.log('usingAwait module'); setTimeout(() => console.log('users:', users), 100); }); 

虽然这种方法似乎是给出了预期的结果,但是有一定的局限性:导入模块必须了解这种模式才能正确使用它

而顶层await就可以解决这些问题:

 // a.js const resp = await fetch('https://jsonplaceholder.typicode.com/users'); const users = resp.json(); export { users}; // usingAwait.js import {users} from './a.mjs'; console.log(users); console.log('usingAwait module'); 

顶级 await 在以下场景中将非常有用:

  • 动态加载模块:
 const strings = await import(`/i18n/${navigator.language}`); 
  • 资源初始化:
const connection = await dbConnector(); 
  • 依赖回退:
let translations; try { translations = await import('https://app.fr.json'); } catch { translations = await import('https://fallback.en.json'); } 

该特性的浏览器支持如下:

2. Object.hasOwn()

在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。

Object.hasOwn 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法:

const example = { property: '123' }; console.log(Object.prototype.hasOwnProperty.call(example, 'property')); console.log(Object.hasOwn(example, 'property')); 

该特性的浏览器支持如下:

3. at()

at() 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:

const array = [0,1,2,3,4,5]; console.log(array[array.length-1]); // 5 console.log(array.at(-1)); // 5 console.log(array[array.lenght-2]); // 4 console.log(array.at(-2)); // 4 

除了数组,字符串也可以使用at()方法进行索引:

const str = "hello world"; console.log(str[str.length - 1]); // d console.log(str.at(-1)); // d 

4. error.cause

在 ECMAScript 2022 规范中,new Error() 中可以指定导致它的原因:

function readFiles(filePaths) { return filePaths.map( (filePath) => { try { // ··· } catch (error) { throw new Error( `While processing ${filePath}`, {cause: error} ); } }); } 

5. 正则表达式匹配索引

该特性允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前,只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下,这是不够的。因此,在这个规范中,如果设置标志 /d,将额外获得一个带有开始和结束索引的数组。

const matchObj = /(a+)(b+)/d.exec('aaaabb'); console.log(matchObj[1]) // 'aaaa' console.log(matchObj[2]) // 'bb' 

由于 /d 标识的存在,matchObj还有一个属性.indices,它用来记录捕获的每个编号组:

console.log(matchObj.indices[1]) // [0, 4] console.log(matchObj.indices[2]) // [4, 6] 

我们还可以使用命名组:

const matchObj = /(?a+)(?b+)/d.exec('aaaabb'); console.log(matchObj.groups.as); // 'aaaa' console.log(matchObj.groups.bs); // 'bb' 

这里给两个字符匹配分别命名为asbs,然后就可以通过groups来获取到这两个命名分别匹配到的字符串。

它们的索引存储在 matchObj.indices.groups 中:

console.log(matchObj.indices.groups.as); // [0, 4] console.log(matchObj.indices.groups.bs); // [4, 6] 

匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题:它指向引用内容的开始和结束位置。

const reQuoted = /“([^”]+)”/dgu; function pointToQuotedText(str) { const startIndices = new Set(); const endIndices = new Set(); for (const match of str.matchAll(reQuoted)) { const [start, end] = match.indices[1]; startIndices.add(start); endIndices.add(end); } let result = ''; for (let index=0; index < str.length; index++) { if (startIndices.has(index)) { result += '['; } else if (endIndices.has(index+1)) { result += ']'; } else { result += ' '; } } return result; } console.log(pointToQuotedText('They said “hello” and “goodbye”.')); // ' [ ] [ ] ' 

6. 类的实例成员

(1)公共实例字段

公共类字段允许我们使用赋值运算符 (=) 将实例属性添加到类定义中。下面是一个计数器的例子:

import React, { Component } from "react"; export class Incrementor extends Component { constructor() { super(); this.state = { count: 0, }; this.increment = this.increment.bind(this); } increment() { this.setState({ count: this.state.count + 1 }); } render() { return (  ); } } 

在这个例子中,在构造函数中定义了实例字段和绑定方法,通过新的类语法,可以使代码更加直观。新的公共类字段语法允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数方法。这样就简化了类的定义,使代码更加简洁、可读:

import React from "react"; export class Incrementor extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); render = () => (  ); } 

有些小伙伴可能就疑问了,这个功能很早就可以使用了呀。但是它现在还不是标准的 ECMAScript,默认是不开启的,如果使用 create-react-app 创建 React 项目,那么它默认是启用的,否则我们必须使用正确的babel插件才能正常使用(@babel/preset-env)。

下面来看看关于公共实例字段的注意事项:

  • 公共实例字段存在于每个创建的类实例上。它们要么是在Object.defineProperty()中添加,要么是在基类中的构造时添加(构造函数主体执行之前执行),要么在子类的super()返回之后添加:
class Incrementor { count = 0 } const instance = new Incrementor(); console.log(instance.count); // 0 
  • 未初始化的字段会自动设置为 undefined
class Incrementor { count } const instance = new Incrementor(); console.assert(instance.hasOwnProperty('count')); console.log(instance.count); // undefined 
  • 可以进行字段的计算:
const PREFIX = 'main'; class Incrementor { [`${PREFIX}Count`] = 0 } const instance = new Incrementor(); console.log(instance.mainCount); // 0 

(2)私有实例字段、方法和访问器

默认情况下,ES6 中所有属性都是公共的,可以在类外检查或修改。下面来看一个例子:

class TimeTracker { name = 'zhangsan'; project = 'blog'; hours = 0; set addHours(hour) { this.hour
                
                

-六神源码网