新聞中心
大家好,我是 CUGGZ。

麻章網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)自2013年起到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
2022 年 6 月 22 日,第 123 屆 ECMA 大會批準(zhǔn)了 ECMAScript 2022 語言規(guī)范,這意味著它現(xiàn)在正式成為標(biāo)準(zhǔn)。下面就來看看 ECMAScript 2022 有哪些新特性!
1、Top-level Await
在ES2017中,引入了 async 函數(shù)和 await 關(guān)鍵字,以簡化 Promise 的使用,但是 await 關(guān)鍵字只能在 async 函數(shù)內(nèi)部使用。嘗試在異步函數(shù)之外使用 await 就會報(bào)錯(cuò):SyntaxError - SyntaxError: await is only valid in async function。
頂層 await 允許我們在 async 函數(shù)外面使用 await 關(guān)鍵字。它允許模塊充當(dāng)大型異步函數(shù),通過頂層 await,這些 ECMAScript 模塊可以等待資源加載。這樣其他導(dǎo)入這些模塊的模塊在執(zhí)行代碼之前要等待資源加載完再去執(zhí)行。
由于 await 僅在 async 函數(shù)中可用,因此模塊可以通過將代碼包裝在 async 函數(shù)中來在代碼中包含 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');
我們還可以立即調(diào)用頂層async函數(shù)(IIAFE):
import fetch from "node-fetch";
(async () => {
const resp = await fetch('https://jsonplaceholder.typicode.com/users');
users = resp.json();
})();
export { users };
這樣會有一個(gè)缺點(diǎn),直接導(dǎo)入的 users 是 undefined,需要在異步執(zhí)行完成之后才能訪問它:
// usingAwait.js
import {users} from './a.js';
console.log('users:', users); // undefined
setTimeout(() => {
console.log('users:', users);
}, 100);
console.log('usingAwait module');
當(dāng)然,這種方法并不安全,因?yàn)槿绻惒胶瘮?shù)執(zhí)行花費(fèi)的時(shí)間超過100毫秒, 它就不會起作用了,users 仍然是 undefined。
另一個(gè)方法是導(dǎo)出一個(gè) promise,讓導(dǎo)入模塊知道數(shù)據(jù)已經(jīng)準(zhǔn)備好了:
//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);
});
雖然這種方法似乎是給出了預(yù)期的結(jié)果,但是有一定的局限性:導(dǎo)入模塊必須了解這種模式才能正確使用它。
而頂層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 在以下場景中將非常有用:
- 動態(tài)加載模塊:
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() 來檢查一個(gè)屬性是否屬于對象。
Object.hasOwn 特性是一種更簡潔、更可靠的檢查屬性是否直接設(shè)置在對象上的方法:
const example = {
property: '123'
};
console.log(Object.prototype.hasOwnProperty.call(example, 'property'));
console.log(Object.hasOwn(example, 'property'));該特性的瀏覽器支持如下:
3、at()
at() 是一個(gè)數(shù)組方法,用于通過給定索引來獲取數(shù)組元素。當(dāng)給定索引為正時(shí),這種新方法與使用括號表示法訪問具有相同的行為。當(dāng)給出負(fù)整數(shù)索引時(shí),就會從數(shù)組的最后一項(xiàng)開始檢索:
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
除了數(shù)組,字符串也可以使用at()方法進(jìn)行索引:
const str = "hello world";
console.log(str[str.length - 1]); // d
console.log(str.at(-1)); // d
4、error.cause
在 ECMAScript 2022 規(guī)范中,new Error() 中可以指定導(dǎo)致它的原因:
function readFiles(filePaths) {
return filePaths.map(
(filePath) => {
try {
// ···
} catch (error) {
throw new Error(
`While processing ${filePath}`,
{cause: error}
);
}
});
}5、正則表達(dá)式匹配索引
該特性允許我們利用 ??d??? 字符來表示我們想要匹配字符串的開始和結(jié)束索引。以前,只能在字符串匹配操作期間獲得一個(gè)包含提取的字符串和索引信息的數(shù)組。在某些情況下,這是不夠的。因此,在這個(gè)規(guī)范中,如果設(shè)置標(biāo)志 ??/d??,將額外獲得一個(gè)帶有開始和結(jié)束索引的數(shù)組。
const matchObj = /(a+)(b+)/d.exec('aaaabb');
console.log(matchObj[1]) // 'aaaa'
console.log(matchObj[2]) // 'bb'由于 /d 標(biāo)識的存在,matchObj還有一個(gè)屬性.indices,它用來記錄捕獲的每個(gè)編號組:
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'
這里給兩個(gè)字符匹配分別命名為as和bs,然后就可以通過groups來獲取到這兩個(gè)命名分別匹配到的字符串。
它們的索引存儲在 matchObj.indices.groups 中:
console.log(matchObj.indices.groups.as); // [0, 4]
console.log(matchObj.indices.groups.bs); // [4, 6]
匹配索引的一個(gè)重要用途就是指向語法錯(cuò)誤所在位置的解析器。下面的代碼解決了一個(gè)相關(guān)問題:它指向引用內(nèi)容的開始和結(jié)束位置。
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)公共實(shí)例字段
公共類字段允許我們使用賦值運(yùn)算符 (=) 將實(shí)例屬性添加到類定義中。下面是一個(gè)計(jì)數(shù)器的例子:
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 (
);
}
}在這個(gè)例子中,在構(gòu)造函數(shù)中定義了實(shí)例字段和綁定方法,通過新的類語法,可以使代碼更加直觀。新的公共類字段語法允許我們直接將實(shí)例屬性作為屬性添加到類上,而無需使用構(gòu)造函數(shù)方法。這樣就簡化了類的定義,使代碼更加簡潔、可讀:
import React from "react";
export class Incrementor extends React.Component {
state = { count: 0 };
increment = () => this.setState({ count: this.state.count + 1 });
render = () => (
);
}
有些小伙伴可能就疑問了,這個(gè)功能很早就可以使用了呀。但是它現(xiàn)在還不是標(biāo)準(zhǔn)的 ECMAScript,默認(rèn)是不開啟的,如果使用 create-react-app 創(chuàng)建 React 項(xiàng)目,那么它默認(rèn)是啟用的,否則我們必須使用正確的babel插件才能正常使用(@babel/preset-env)。
下面來看看關(guān)于公共實(shí)例字段的注意事項(xiàng):
- 公共實(shí)例字段存在于每個(gè)創(chuàng)建的類實(shí)例上。它們要么是在Object.defineProperty()?中添加,要么是在基類中的構(gòu)造時(shí)添加(構(gòu)造函數(shù)主體執(zhí)行之前執(zhí)行),要么在子類的super()返回之后添加:
class Incrementor {
count = 0
}
const instance = new Incrementor();
console.log(instance.count); // 0- 未初始化的字段會自動設(shè)置為undefined:
class Incrementor {
count
}
const instance = new Incrementor();
console.assert(instance.hasOwnProperty('count'));
console.log(instance.count); // undefined- 可以進(jìn)行字段的計(jì)算:
const PREFIX = 'main';
class Incrementor {
[`${PREFIX}Count`] = 0
}
const instance = new Incrementor();
console.log(instance.mainCount); // 0
(2)私有實(shí)例字段、方法和訪問器
默認(rèn)情況下,ES6 中所有屬性都是公共的,可以在類外檢查或修改。下面來看一個(gè)例子:
class TimeTracker {
name = 'zhangsan';
project = 'blog';
hours = 0;
set addHours(hour) {
this.hours += hour;
}
get timeSheet() {
return `${this.name} works ${this.hours || 'nothing'} hours on ${this.project}`;
}
}
let person = new TimeTracker();
person.addHours = 2; // 標(biāo)準(zhǔn) setter
person.hours = 4; // 繞過 setter 進(jìn)行設(shè)置
person.timeSheet;可以看到,在類中沒有任何措施可以防止在不調(diào)用 setter 的情況下更改屬性。
而私有類字段將使用哈希#前綴定義,從上面的示例中,可以修改它以包含私有類字段,以防止在類方法之外更改屬性:
class TimeTracker {
name = 'zhangsan';
project = 'blog';
#hours = 0; // 私有類字段
set addHours(hour) {
this.#hours += hour;
}
get timeSheet() {
return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
}
}
let person = new TimeTracker();
person.addHours = 4; // 標(biāo)準(zhǔn) setter
person.timeSheet // zhangsan works 4 hours on blog當(dāng)嘗試在 setter 方法之外修改私有類字段時(shí),就會報(bào)錯(cuò):
person.hours = 4 // Error Private field '#hours' must be declared in an enclosing class
還可以將方法或 getter/setter 設(shè)為私有,只需要給這些方法名稱前面加#即可:
class TimeTracker {
name = 'zhangsan';
project = 'blog';
#hours = 0; // 私有類字段
set #addHours(hour) {
this.#hours += hour;
}
get #timeSheet() {
return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
}
constructor(hours) {
this.#addHours = hours;
console.log(this.#timeSheet);
}
}
let person = new TimeTracker(4); // zhangsan works 4 hours on blog由于嘗試訪問對象上不存在的私有字段會發(fā)生異常,因此需要能夠檢查對象是否具有給定的私有字段??梢允褂?nbsp;in 運(yùn)算符來檢查對象上是否有私有字段:
class Example {
#field
static isExampleInstance(object) {
return #field in object;
}
}(3)靜態(tài)公共字段
在ES6中,不能在類的每個(gè)實(shí)例中訪問靜態(tài)字段或方法,只能在原型中訪問。ES 2022 提供了一種在 JavaScript 中使用 static 關(guān)鍵字聲明靜態(tài)類字段的方法。下面來看一個(gè)例子:
class Shape {
static color = 'blue';
static getColor() {
return this.color;
}
getMessage() {
return `color:${this.color}` ;
}
}可以從類本身訪問靜態(tài)字段和方法:
console.log(Shape.color); // blue
console.log(Shape.getColor()); // blue
console.log('color' in Shape); // true
console.log('getColor' in Shape); // true
console.log('getMessage' in Shape); // false
實(shí)例不能訪問靜態(tài)字段和方法:
const shapeInstance = new Shape();
console.log(shapeInstance.color); // undefined
console.log(shapeInstance.getColor); // undefined
console.log(shapeInstance.getMessage());// color:undefined
靜態(tài)字段只能通過靜態(tài)方法訪問:
console.log(Shape.getColor()); // blue
console.log(Shape.getMessage()); //TypeError: Shape.getMessage is not a function
這里的 Shape.getMessage() 就報(bào)錯(cuò)了,因?yàn)?nbsp;getMessage 不是一個(gè)靜態(tài)函數(shù),所以它不能通過類名 Shape 訪問??梢酝ㄟ^以下方式來解決這個(gè)問題:
getMessage() {
return `color:${Shape.color}` ;
}靜態(tài)字段和方法是從父類繼承的:
class Rectangle extends Shape { }
console.log(Rectangle.color); // blue
console.log(Rectangle.getColor()); // blue
console.log('color' in Rectangle); // true
console.log('getColor' in Rectangle); // true
console.log('getMessage' in Rectangle); // false(4)靜態(tài)私有字段和方法
與私有實(shí)例字段和方法一樣,靜態(tài)私有字段和方法也使用哈希 (#) 前綴來定義:
class Shape {
static #color = 'blue';
static #getColor() {
return this.#color;
}
getMessage() {
return `color:${Shape.#getColor()}` ;
}
}
const shapeInstance = new Shape();
shapeInstance.getMessage(); // color:blue私有靜態(tài)字段有一個(gè)限制:只有定義私有靜態(tài)字段的類才能訪問該字段。這可能在使用 this 時(shí)導(dǎo)致出乎意料的情況:
class Shape {
static #color = 'blue';
static #getColor() {
return this.#color;
}
static getMessage() {
return `color:${this.#color}` ;
}
getMessageNonStatic() {
return `color:${this.#getColor()}` ;
}
}
class Rectangle extends Shape {}
console.log(Rectangle.getMessage()); // Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it
const rectangle = new Rectangle();
console.log(rectangle.getMessageNonStatic()); // TypeError: Cannot read private member #getColor from an object whose class did not declare it在這個(gè)例子中,this 指向的是 Rectangle 類,它無權(quán)訪問私有字段 #color。當(dāng)我們嘗試調(diào)用 Rectangle.getMessage() 時(shí),它無法讀取 #color 并拋出了 TypeError??梢赃@樣來進(jìn)行修改:
class Shape {
static #color = 'blue';
static #getColor() {
return this.#color;
}
static getMessage() {
return `${Shape.#color}`;
}
getMessageNonStatic() {
return `color:${Shape.#getColor()} color`;
}
}
class Rectangle extends Shape {}
console.log(Rectangle.getMessage()); // color:blue
const rectangle = new Rectangle();
console.log(rectangle.getMessageNonStatic()); // color:blue(5)類靜態(tài)初始化塊
靜態(tài)私有和公共字段只能讓我們在類定義期間執(zhí)行靜態(tài)成員的每個(gè)字段初始化。如果我們需要在初始化期間像 try…catch 一樣進(jìn)行異常處理,就不得不在類之外編寫此邏輯。該規(guī)范就提供了一種在類聲明/定義期間評估靜態(tài)初始化代碼塊的優(yōu)雅方法,可以訪問類的私有字段。
先來看一個(gè)例子:
class Person {
static GENDER = "Male"
static TOTAL_EMPLOYED;
static TOTAL_UNEMPLOYED;
try {
// ...
} catch {
// ...
}
}上面的代碼就會引發(fā)錯(cuò)誤,可以使用類靜態(tài)塊來重構(gòu)它,只需將try...catch包裹在 static 中即可:
class Person {
static GENDER = "Male"
static TOTAL_EMPLOYED;
static TOTAL_UNEMPLOYED;
static {
try {
// ...
} catch {
// ...
}
}
}此外,類靜態(tài)塊提供對詞法范圍的私有字段和方法的特權(quán)訪問。這里需要在具有實(shí)例私有字段的類和同一范圍內(nèi)的函數(shù)之間共享信息的情況下很有用。
let getData;
class Person {
#x
constructor(x) {
this.#x = { data: x };
}
static {
getData = (obj) => obj.#x;
}
}
function readPrivateData(obj) {
return getDa
本文標(biāo)題:EcmaScript2022正式發(fā)布,有哪些新特性?
當(dāng)前URL:http://m.fisionsoft.com.cn/article/djjdoig.html


咨詢
建站咨詢
