11 個(gè) ES2022(ES13)中驚人的 JavaScript 新特性

英文 | https://javascript.plainenglish.io/es13-javascript-features-eed7ed2f1497
翻譯 | 楊小愛
class Car {constructor() {this.color = 'blue';this.age = 2;}}const car = new Car();console.log(car.color); // blueconsole.log(car.age); // 2
ES13 消除了這個(gè)限制,現(xiàn)在我們可以編寫如下代碼:
class Car {color = 'blue';age = 2;}const car = new Car();console.log(car.color); // blueconsole.log(car.age); // 2
2、私有方法和字段
以前,不能在類中聲明私有成員,成員通常以下劃線 (_) 為前綴,表示它是私有的,但仍然可以從類外部訪問和修改。
class Person {_firstName = 'Joseph';_lastName = 'Stevens';get name() {return `${this._firstName} ${this._lastName}`;}}const person = new Person();console.log(person.name); // Joseph Stevens// Members intended to be private can still be accessed// from outside the classconsole.log(person._firstName); // Josephconsole.log(person._lastName); // Stevens// They can also be modifiedperson._firstName = 'Robert';person._lastName = 'Becker';console.log(person.name); // Robert Becker
使用 ES13,我們現(xiàn)在可以將私有字段和成員添加到類中,方法是在其前面加上井號 (#),試圖從類外部訪問它們會導(dǎo)致錯誤:
class Person {get name() {return `${this.}}const person = new Person();console.log(person.name);// SyntaxError: Private field '#firstName' must be// declared in an enclosing classconsole.log(person.console.log(person.
請注意,這里拋出的錯誤是語法錯誤,發(fā)生在編譯時(shí),因此沒有部分代碼運(yùn)行,編譯器甚至不希望您嘗試從類外部訪問私有字段,因此,它假定您正在嘗試聲明一個(gè)。
3、await運(yùn)算符
在 JavaScript 中,await 運(yùn)算符用于暫停執(zhí)行,直到 Promise 被解決(履行或拒絕)。
以前,我們只能在 async 函數(shù)中使用此運(yùn)算符 - 使用 async 關(guān)鍵字聲明的函數(shù)。我們無法在全球范圍內(nèi)這樣做。
function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();}, timeout);});}// SyntaxError: await is only valid in async functionsawait setTimeoutAsync(3000);
使用 ES13,現(xiàn)在我們可以:
function setTimeoutAsync(timeout) {return new Promise((resolve) => {setTimeout(() => {resolve();}, timeout);});}// Waits for timeout - no error thrownawait setTimeoutAsync(3000);
4、靜態(tài)類字段和靜態(tài)私有方法
我們現(xiàn)在可以在 ES13 中為類聲明靜態(tài)字段和靜態(tài)私有方法,靜態(tài)方法可以使用 this 關(guān)鍵字訪問類中的其他私有/公共靜態(tài)成員,實(shí)例方法可以使用 this.constructor 訪問它們。
class Person {staticstatic getCount() {return this.}constructor() {this.constructor.}staticthis.}}const person1 = new Person();const person2 = new Person();console.log(Person.getCount()); // 2
5、類靜態(tài)塊
ES13 允許在創(chuàng)建類時(shí)定義只執(zhí)行一次的靜態(tài)塊,這類似于其他支持面向?qū)ο缶幊痰恼Z言(如 C# 和 Java)中的靜態(tài)構(gòu)造函數(shù)。
一個(gè)類的類主體中可以有任意數(shù)量的靜態(tài) {} 初始化塊,它們將與任何交錯的靜態(tài)字段初始值設(shè)定項(xiàng)一起按照聲明的順序執(zhí)行,我們可以在靜態(tài)塊中使用超屬性來訪問超類的屬性。
class Vehicle {static defaultColor = 'blue';}class Car extends Vehicle {static colors = [];static {this.colors.push(super.defaultColor, 'red');}static {this.colors.push('green');}}console.log(Car.colors); // [ 'blue', 'red', 'green' ]
6、私人領(lǐng)域的人體工程學(xué)品牌檢查
我們可以使用這個(gè)新特性來檢查一個(gè)對象中是否有一個(gè)特定的私有字段,使用 in 運(yùn)算符。
class Car {hasColor() {return #color in this;}}const car = new Car();console.log(car.hasColor()); // true;
in 運(yùn)算符可以正確區(qū)分不同類的同名私有字段:
class Car {hasColor() {return #color in this;}}class House {hasColor() {return #color in this;}}const car = new Car();const house = new House();console.log(car.hasColor()); // true;console.log(car.hasColor.call(house)); // falseconsole.log(house.hasColor()); // trueconsole.log(house.hasColor.call(car)); // false
7、at() 方法進(jìn)行索引
我們通常在 JavaScript 中使用方括號 ([]) 來訪問數(shù)組的第 N 個(gè)元素,這通常是一個(gè)簡單的過程,我們只訪問數(shù)組的 N - 1 屬性。
const arr = ['a', 'b', 'c', 'd'];console.log(arr[1]); // b
但是,如果我們想使用方括號訪問數(shù)組末尾的第 N 個(gè)項(xiàng)目,我們必須使用 arr.length - N 的索引。
const arr = ['a', 'b', 'c', 'd'];// 1st element from the endconsole.log(arr[arr.length - 1]); // d// 2nd element from the endconsole.log(arr[arr.length - 2]); // c
新的 at() 方法讓我們可以更簡潔、更有表現(xiàn)力地做到這一點(diǎn),要訪問數(shù)組末尾的第 N 個(gè)元素,我們只需將負(fù)值 -N 傳遞給 at()。
const arr = ['a', 'b', 'c', 'd'];// 1st element from the endconsole.log(arr.at(-1)); // d// 2nd element from the endconsole.log(arr.at(-2)); // c
除了數(shù)組,字符串和 TypedArray 對象現(xiàn)在也有 at() 方法。
const str = 'Coding Beauty';console.log(str.at(-1)); // yconsole.log(str.at(-2)); // tconst typedArray = new Uint8Array([16, 32, 48, 64]);console.log(typedArray.at(-1)); // 64console.log(typedArray.at(-2)); // 48
8、 RegExp 匹配索引
這個(gè)新功能允許我們指定我們想要獲取給定字符串中 RegExp 對象匹配的開始和結(jié)束索引。
以前,我們只能在字符串中獲取正則表達(dá)式匹配的起始索引。
const str = 'sun and moon';const regex = /and/;const matchObj = regex.exec(str);// [ 'and', index: 4, input: 'sun and moon', groups: undefined ]console.log(matchObj);
我們現(xiàn)在可以指定一個(gè) d 正則表達(dá)式標(biāo)志來獲取匹配開始和結(jié)束的兩個(gè)索引。
const str = 'sun and moon';const regex = /and/d;const matchObj = regex.exec(str);/**['and',index: 4,input: 'sun and moon',groups: undefined,indices: [ [ 4, 7 ], groups: undefined ]]*/console.log(matchObj);
設(shè)置 d 標(biāo)志后,返回的對象將具有包含開始和結(jié)束索引的 indices 屬性。
9、Object.hasOwn() 方法
在 JavaScript 中,我們可以使用 Object.prototype.hasOwnProperty() 方法來檢查對象是否具有給定的屬性。
class Car {color = 'green';age = 2;}const car = new Car();console.log(car.hasOwnProperty('age')); // trueconsole.log(car.hasOwnProperty('name')); // false
但是,這種方法存在一定的問題,一方面,Object.prototype.hasOwnProperty() 方法不受保護(hù) - 它可以通過為類定義自定義 hasOwnProperty() 方法來覆蓋,該方法可能具有與 Object.prototype.hasOwnProperty() 完全不同的行為。
class Car {color = 'green';age = 2;// This method does not tell us whether an object of// this class has a given property.hasOwnProperty() {return false;}}const car = new Car();console.log(car.hasOwnProperty('age')); // falseconsole.log(car.hasOwnProperty('name')); // false
另一個(gè)問題是,對于使用 null 原型創(chuàng)建的對象(使用 Object.create(null)),嘗試對其調(diào)用此方法會導(dǎo)致錯誤。
const obj = Object.create(null);obj.color = 'green';obj.age = 2;// TypeError: obj.hasOwnProperty is not a functionconsole.log(obj.hasOwnProperty('color'));
解決這些問題的一種方法是使用調(diào)用 Object.prototype.hasOwnProperty Function 屬性上的 call() 方法,如下所示:
const obj = Object.create(null);obj.color = 'green';obj.age = 2;obj.hasOwnProperty = () => false;console.log(Object.prototype.hasOwnProperty.call(obj, 'color')); // trueconsole.log(Object.prototype.hasOwnProperty.call(obj, 'name')); // false
這不是很方便,我們可以編寫一個(gè)可重用的函數(shù)來避免重復(fù)自己:
function objHasOwnProp(obj, propertyKey) {return Object.prototype.hasOwnProperty.call(obj, propertyKey);}const obj = Object.create(null);obj.color = 'green';obj.age = 2;obj.hasOwnProperty = () => false;console.log(objHasOwnProp(obj, 'color')); // trueconsole.log(objHasOwnProp(obj, 'name')); // false
不過沒有必要,因?yàn)槲覀兛梢允褂眯碌膬?nèi)置 Object.hasOwn() 方法。與我們的可重用函數(shù)一樣,它接受對象和屬性作為參數(shù),如果指定的屬性是對象的直接屬性,則返回 true。否則,它返回 false。
const obj = Object.create(null);obj.color = 'green';obj.age = 2;obj.hasOwnProperty = () => false;console.log(Object.hasOwn(obj, 'color')); // trueconsole.log(Object.hasOwn(obj, 'name')); // false
10、錯誤原因
錯誤對象現(xiàn)在有一個(gè) cause 屬性,用于指定導(dǎo)致即將拋出的錯誤的原始錯誤。這有助于為錯誤添加額外的上下文信息并幫助診斷意外行為,我們可以通過在作為第二個(gè)參數(shù)傳遞給 Error() 構(gòu)造函數(shù)的對象上設(shè)置 cause 屬性來指定錯誤的原因。
function userAction() {try {apiCallThatCanThrow();} catch (err) {throw new Error('New error message', { cause: err });}}try {userAction();} catch (err) {console.log(err);console.log(`Cause by: ${err.cause}`);}
11、從最后一個(gè)數(shù)組查找
在 JavaScript 中,我們已經(jīng)可以使用 Array find() 方法在數(shù)組中查找通過指定測試條件的元素,同樣,我們可以使用 findIndex() 來查找此類元素的索引。
雖然 find() 和 findIndex() 都從數(shù)組的第一個(gè)元素開始搜索,但在某些情況下,最好從最后一個(gè)元素開始搜索。
在某些情況下,我們知道從最后一個(gè)元素中查找可能會獲得更好的性能。例如,這里我們試圖在數(shù)組中獲取值 prop 等于 y 的項(xiàng)目。使用 find() 和 findIndex():
const letters = [{ value: 'v' },{ value: 'w' },{ value: 'x' },{ value: 'y' },{ value: 'z' },];const found = letters.find((item) => item.value === 'y');const foundIndex = letters.findIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }console.log(foundIndex); // 3
這行得通,但是由于目標(biāo)對象更靠近數(shù)組的尾部,如果我們使用 findLast() 和 findLastIndex() 方法從末尾搜索數(shù)組,我們可以讓這個(gè)程序運(yùn)行得更快。
const letters = [{ value: 'v' },{ value: 'w' },{ value: 'x' },{ value: 'y' },{ value: 'z' },];const found = letters.findLast((item) => item.value === 'y');const foundIndex = letters.findLastIndex((item) => item.value === 'y');console.log(found); // { value: 'y' }console.log(foundIndex); // 3
另一個(gè)用例可能要求我們專門從末尾搜索數(shù)組以獲取正確的項(xiàng)目。例如,如果我們想在數(shù)字列表中查找最后一個(gè)偶數(shù), find() 和 findIndex() 會產(chǎn)生錯誤的結(jié)果:
const nums = [7, 14, 3, 8, 10, 9];// gives 14, instead of 10const lastEven = nums.find((value) => value % 2 === 0);// gives 1, instead of 4const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);console.log(lastEven); // 14console.log(lastEvenIndex); // 1
我們可以在調(diào)用 find() 和 findIndex() 之前調(diào)用數(shù)組的 reverse() 方法來反轉(zhuǎn)元素的順序。
但是這種方法會導(dǎo)致數(shù)組發(fā)生不必要的突變,因?yàn)?reverse() 會反轉(zhuǎn)數(shù)組的元素。避免這種突變的唯一方法是制作整個(gè)數(shù)組的新副本,這可能會導(dǎo)致大型數(shù)組出現(xiàn)性能問題。
此外, findIndex() 仍然無法在反轉(zhuǎn)數(shù)組上工作,因?yàn)榉崔D(zhuǎn)元素也意味著更改它們在原始數(shù)組中的索引。要獲得原始索引,我們需要執(zhí)行額外的計(jì)算,這意味著編寫更多代碼。
const nums = [7, 14, 3, 8, 10, 9];// Copying the entire array with the spread syntax before// calling reverse()const reversed = [...nums].reverse();// correctly gives 10const lastEven = reversed.find((value) => value % 2 === 0);// gives 1, instead of 4const reversedIndex = reversed.findIndex((value) => value % 2 === 0);// Need to re-calculate to get original indexconst lastEvenIndex = reversed.length - 1 - reversedIndex;console.log(lastEven); // 10console.log(reversedIndex); // 1console.log(lastEvenIndex); // 4
在 findLast() 和 findLastIndex() 方法派上用場的情況下。
const nums = [7, 14, 3, 8, 10, 9];const lastEven = nums.findLast((num) => num % 2 === 0);const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);console.log(lastEven); // 10console.log(lastEvenIndex); // 4
這段代碼更短,更易讀。最重要的是,它會產(chǎn)生正確的結(jié)果。
結(jié)論
所以我們已經(jīng)看到了 ES13 為 JavaScript 帶來的最新特性,使用它們來提高我們作為開發(fā)人員的工作效率,并以更簡潔和清晰的方式編寫更簡潔的代碼。如果你覺得我這篇文章對你有用的話,請記得點(diǎn)贊我,關(guān)注我,并將它分享給你身邊的朋友,也許能夠幫助到他。
最后,感謝你的閱讀。
學(xué)習(xí)更多技能
請點(diǎn)擊下方公眾號
![]()

