圍觀!我國(guó)第一個(gè)推進(jìn)到瀏覽器標(biāo)準(zhǔn)的 ECMaScript 提案!
以下文章來(lái)源于前端很美 ,作者ginkgo6
在2022年6月22日,第123屆Ecma大會(huì)批準(zhǔn)了ECMAScript 2022語(yǔ)言規(guī)范。一起來(lái)看看ES13給我們帶來(lái)了哪些新特性吧。其中最后一個(gè)特性,是我國(guó)第一個(gè)推進(jìn)到瀏覽器標(biāo)準(zhǔn)的EcmaScript提案。
1.私有屬性和私有方法
之前js中class的一個(gè)痛點(diǎn)就是沒(méi)有私有屬性,畢竟封裝是面向?qū)ο蟮娜筇匦?,沒(méi)有私有屬性如何實(shí)現(xiàn)封裝。盡管我們可以用很多方法去曲線實(shí)現(xiàn)私有屬性比如閉包proxy、weakmap、symbol等等,但是他們各自有各自的缺陷,下面的代碼演示其中以_作為命名約定的方式實(shí)現(xiàn)私有屬性的缺點(diǎn)。
class User {
constructor() {
// public 屬性
this.name = "Tom";
// private 屬性
this._lastName = "Brown";
}
getFullName(){
return `${this.name} ${this._lastName}`
}
}
const user = new User();
user.name
// "Tom"
user._lastName
// "Brown"
// no error thrown, we can access it from outside the class
在構(gòu)造函數(shù)中,我們定義了兩個(gè)字段,其中一個(gè)字段是以_開(kāi)頭的, 這是一個(gè)命名約定用于將字段聲明為私有字段,但是當(dāng)我們?cè)陬愅饷嬖L問(wèn)該字段時(shí),并沒(méi)有引起報(bào)錯(cuò),原因是因?yàn)樗皇且粋€(gè)命名規(guī)范而已。
現(xiàn)在在ES13中,我們可以用更簡(jiǎn)單的方法聲明公共和私有字段,第一,我們不必在構(gòu)造函數(shù)中定義它們了,其次,我們可以通過(guò)在字段前面添加#號(hào)來(lái)定義一個(gè)真正的私有字段。
class User {
name = "Tom";
#lastName = "Brown"
getFullName(){
return `${this.name} ${this.#lastName}`
}
}
const user = new User();
user.name
// "Tom"
user.getFullName();
// "Tom Brown"
user.#lastName
// SyntaxError - cannot be accessed or modified from outside the class
有了這個(gè)特性,我們終于可以簡(jiǎn)單輕松的定義一個(gè)真正的私有屬性或私有方法了。
2.私有屬性檢測(cè)
當(dāng)我們嘗試訪問(wèn)對(duì)象上不存在的私有屬性時(shí)會(huì)報(bào)錯(cuò),所以就需要一種方法來(lái)檢測(cè)對(duì)象是否具有某私有屬性,此時(shí),我們可以在class中使用in關(guān)鍵字來(lái)完成此工作。
class Person {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
Person.check(new Person()), // true
當(dāng)然,你可能會(huì)發(fā)問(wèn)對(duì)于具有相同名稱的私有屬性的類,in 關(guān)鍵字是否能正常工作,別擔(dān)心,請(qǐng)參考以下例子,User類和Person類具有相同的私有屬性name,in關(guān)鍵字可以正確的區(qū)分。
class User {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
class Person {
#name;
constructor(name) {
this.#name = name;
}
static check(obj) {
return #name in obj;
}
}
User.check(new User()), // true
User.check(new Person()), // false
Person.check(new Person()), // true
Person.check(new User()), // false
3. 類靜態(tài)初始化塊(Class Static Block)
類在初始化的時(shí)候會(huì)執(zhí)行靜態(tài)初始化塊(Class Static Block),一個(gè)類可以擁有任意個(gè)靜態(tài)初始化塊,它們將會(huì)按照聲明的順序執(zhí)行。靜態(tài)初始?jí)K內(nèi)還可以訪問(wèn)父類的靜態(tài)屬性。
class Dictionary {
static words = [ "yes", "no", "maybe" ];
}
class Words extends Dictionary {
static englishWords = [];
static #localWord = 'ok';
// 第一個(gè)靜態(tài)初始化塊
static {
const words = super.words;
this.englishWords.push(...words);
}
// 第二個(gè)靜態(tài)初始化塊
static {
this.englishWords.push(this.#localWord);
}
}
console.log(Words.englishWords)
//輸出 -> ["yes", "no", "maybe", "ok"]
下面這個(gè)例子,我們還可以通過(guò)靜態(tài)初始化塊將私有屬性暴露出去。
let getClassPrivateField;
class Person {
#privateField;
constructor(value) {
this.#privateField = value;
}
static {
getClassPrivateField = (obj) => obj.#privateField;
}
}
getClassPrivateField(new Person('private value'));
4.Regexp Match Indices
之前 ECMAScript 中的 「RegExp.prototype.exec」 方法的返回值已經(jīng)提供了對(duì)于匹配的捕獲組 文本與對(duì)應(yīng)的捕獲組在正則表達(dá)式中的索引。盡管它已經(jīng)包含輸入字符串,模式匹配的索引以及包含任何已命名捕獲組的子字符串的group對(duì)象,但是對(duì)于復(fù)雜情況,此信息可能不足。
const fruits = 'Fruits: apple, banana, orange'
const regex = /(banana)/g;
const matchObj = regex.exec(fruits);
console.log(matchObj);
// [
// 'banana',
// 'banana',
// index: 15,
// input: 'Fruits: apple, banana, orange',
// groups: undefined
// ]
所以ES13通過(guò)新增/d修飾符,向匹配結(jié)果添加一個(gè)屬性 .indices,此屬性是一個(gè)索引數(shù)組,其中包含每個(gè)捕獲的子字符串的一對(duì)開(kāi)始索引和結(jié)束索引。
const fruits = 'Fruits: apple, banana, orange'
const regex = /(banana)/gd;
const matchObj = regex.exec(fruits);
console.log(matchObj);
// [
// 'banana',
// 'banana',
// index: 15,
// indices:[
// [15, 21],
// [15, 21]
// ]
// input: 'Fruits: apple, banana, orange',
// groups: undefined
// ]
5.Await operator at the top-level
之前await關(guān)鍵詞只能在aysnc function里進(jìn)行使用,想要在頂層使用await就必須要加個(gè)aysnc自執(zhí)行函數(shù),這樣十分的不方便, 所以ES13中,引入了可以直接在頂層使用Await關(guān)鍵字的特性。
動(dòng)態(tài)加載模塊:
const strings = await import(`./example.mjs`); |
依賴回退:
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}
加載最快的資源
const resource = await Promise.any([
fetch('http://example1.com'),
fetch('http://example2.com'),
]);
6 .at()方法
之前,我們?nèi)绻胍@取一個(gè)數(shù)組的元素,如果是正序獲取一個(gè)元素,我們可以采取arr[0]這樣的方式, 但是如果我們要倒序訪問(wèn)一個(gè)數(shù)組的元素,就需要采取arr[arr.length - 2]這樣的方式,數(shù)組的名稱arr我們寫了兩次,而且還要寫個(gè)length,這很不優(yōu)雅,復(fù)雜的情況下就更是難受。
const arr = [100,200,300,400]
arr[0] // 100
arr[arr.length - 2] // 300
arr.slice(-2)[0] // 300
為了解決這個(gè)問(wèn)題, ES13引入了at()方法,無(wú)論正序還是倒序獲取元素都非常的優(yōu)雅,有了這個(gè)方法,我們可以在數(shù)組、字符串、TypedArray上通過(guò)索引值方便的獲取元素。
const arr = [100,200,300,400]
arr.at(0) // 100
arr.at(-2) // 300
const str = "ABCD"
str.at(-1) // 'D'
str.at(0) // 'A'
7.Object.hasOwn(obj, propKey)
之前我們?nèi)绻胍袛嗄硨?duì)象是否具有某屬性,我們會(huì)通過(guò)in和obj.hasOwnProperty,但是如果指定的屬性位于原型鏈中,“in”運(yùn)算符也會(huì)返回true。所以之前要想判斷對(duì)象自身是否具有某屬性我們一般都通過(guò)obj.hasOwnProperty來(lái)判斷。
但是obj.hasOwnProperty也有自身的缺點(diǎn):因?yàn)閔asOwnProperty是不受保護(hù)的屬性,所以,人們可能會(huì)在對(duì)象上定義個(gè)自己的hasOwnProperty,如下所示,自定義的hasOwnProperty永遠(yuǎn)返回false,這是一個(gè)坑。
const person = {
name: "Roman",
hasOwnProperty:()=> {
return false
}
}
person.hasOwnProperty('name'); // false
另一個(gè)問(wèn)題是使用Object.create(null) 創(chuàng)建一個(gè)不繼承自 Object.prototype 的對(duì)象,使 hasOwnProperty 方法時(shí)會(huì)報(bào)錯(cuò)。
Object.create(null).hasOwnProperty("name")
// Uncaught TypeError: Object.create(...).hasOwnProperty is not a function
所以,為了解決上面的問(wèn)題,ES13引入了**Object.hasOwn(obj, propKey)**。用法如下所示:
const object = { name: "Mark" };
Object.hasOwn(object, "name"); // true
const object2 = Object.create({ name: "Roman" });
Object.hasOwn(object2, "name"); // false
Object.hasOwn(object2.__proto__, "name"); // true
const object3 = Object.create(null);
Object.hasOwn(object3, "name"); // false
8.Error Cause
Error Cause提案由阿里巴巴的「昭朗」同學(xué)負(fù)責(zé),是我國(guó)第一個(gè)成為瀏覽器標(biāo)準(zhǔn)的EcmaScript提案。
以前因?yàn)?Error Cause 沒(méi)有標(biāo)準(zhǔn)化的參數(shù)定義及官方實(shí)現(xiàn),所以容易丟失 error 的屬性或需要寫比較多的代碼自定義等,并且開(kāi)發(fā)者工具也難以依賴于非語(yǔ)言特性的自定義方案。
try {
apiCallThatCanThrow();
} catch (err) {
throw new Error('New error message', { cause: err });
}
有了這個(gè)新特性,借助cause屬性,我們可以記下導(dǎo)致這個(gè)Error的前一個(gè)Error對(duì)象,錯(cuò)誤對(duì)象就可以以一種簡(jiǎn)單優(yōu)雅的方式鏈接起來(lái)。
9.總結(jié)
以上就是ES13的新特性,新特性會(huì)給js生態(tài)帶來(lái)更多好的東西,提高開(kāi)發(fā)者的效率和體驗(yàn)。
作者:ginkgo6
歡迎關(guān)注微信公眾號(hào) :前端印象