簡介
ES11是ECMA協會在2020年6月發行的一個版本,因為是ECMAScript的第十一個版本,所以也稱為ES11.
今天我們講解一下ES11的新特性。
ES11引入了9個新特性,我們接下來一一講解。
動態imports
在ES11之前,我們可以使用下面的方式進行模組的匯入:
import * as TestModule from "./test-module.js";
但是上面的匯入方式會有一些問題,首先是效率的問題,所有的module都需要在首次載入的時候匯入,會導致程式效率的降低。另外上面的模組名字是寫死的,不可以在程式執行的時候進行動態修改。
也就是說上面的模組匯入方式,不能對模組進行動態匯入,或者按需匯入,在使用上有諸多的不便。
為了解決這個問題,ES11引入了新的import() 方法,使用這個方法,你可以對模組進行動態匯入,並且通過設定模組名為變數的形式,可以對模組名進行動態修改,非常的神奇。我們看一個具體的使用例子:
const baseModulePath = "./baseModules";
const btnImportModule = document.getElementById("btnImportModule");
let userList = [];
btnImportModule.addEventListener("click", async e => {
const userModule = await import(`${baseModulePath}/users.js`);
userList = userModule.getUsers();
});
上面程式碼中我們定義了一個基本的Module路徑,通過點選頁面上的按鈕,可以動態的載入一個users.js模組,然後呼叫該模組的getUsers()方法,獲得userList列表。
import.meta
除了動態引入模組之外,import還提供了一個元屬性meta,它包含了當前引入的模組的資訊,目前他裡面有一個url屬性,代表模組被引用的URL。如果想使用URL資訊,那麼可以在程式碼中使用import.meta.url。
export加強
import是在ECMAScript 2015中引入的,主要被用來做模組的引入,import可以引入整個模組,也可以引入部分模組。如下所示:
import {something} from "./test-module.js";
import * from "./test-module.js";
和import對應的就是export,同樣可以export所有或者部分,如下所示:
export {something} from "./test-module.js";
export * from "./test-module.js";
通常情況來說,上面講的import和export已經夠用了,但是對於匯出模組需要重新命名的情況,我們不能直接匯出,而是必須先在import的時候進行重新命名,然後再使用export將重新命名的模組匯出:
import * as myModule from "./test-module.js";
export {myModule};
如果使用export的增強,則不需要使用import,直接使用export匯出即可:
export * as {myModule} from "./test-module.js";
BigInt
ES11引入了新的資料型別BigInt,在這之前,javascript中表示數字的物件是Number,它可以表示64-bit的浮點型別數字。當然它也可以代表整數,但是整數表示的最大值是2^53,也可以用Number.MAX_SAFE_INTEGER來表示。
一般來說Number已經夠用了,但是如果在某些情況下需要對64-bit的整數進行儲存或者運算,或者要表示的範圍超過了64-bit的話,Number就不夠用了。
怎麼辦呢?如果只是儲存的話,可以儲存為字串,但是第二種字串就不適用了。於是引入了BigInt來解決這個問題。要表示BigInt,只需要在數字的後面加上n即可。
const bigInt = 112233445566778899n;
或者使用建構函式來構造bigInt:
const bigInt = BigInt("112233445566778899");
可以使用typeof來檢視bigInt的型別。要注意的是雖然Number和BigInt都代表的是數字,但是兩者是不能混用的,你不能將一個Number和一個BigInt相加。這會報TypeError異常。
如果非要進行操作,那麼可以使用BigInt建構函式將Number轉換成為BigInt之後再進行。
matchAll()
正則表示式的匹配是一個非常常見的操作,通常我們使用regExp.exec來進行正則的匹配,舉個正則匹配的例子如下:
const regExp = /yyds(\d+)/g;
const text = 'yyds1 is a very good yyds2';
let matches;
while ((matches = regExp.exec(text)) !== null) {
console.log(matches);
}
上面的程式碼執行結果如下:
["yyds1","1"]
["yyds2","2"]
我們得到了所有匹配的值。不過需要使用一個迴圈來進行遍歷,使用起來有諸多的不便,為了簡單起見,ES11引入了matchAll()方法。這個方法可以簡單的返回一個遍歷器,通過遍歷這個遍歷器,就可以得到所有的匹配的值,如下所示:
const regExp = /yyds(\d+)/g;
const text = 'yyds1 is a very good yyds2';
let matches = [...text.matchAll(regExp)];
for (const match of matches) {
console.log(match);
}
globalThis
對於javascript來說,不同的環境對應的全域性物件的獲取方式也是不同的,對於瀏覽器來說通常使用的是window,但是在web worker中使用的是self,而在nodejs中使用的是global。
為了解決在不同環境中的全域性物件不同的問題,ES11引入了globalThis,通過這個全域性物件,程式設計師就不用再去區分到底是在哪個環境下了,只需要使用globalThis即可。
Promise.allSettled()
自從Promise引入之後,有兩個方法可以對Promise進行組合,分別是Promise.all() 和Promise.race(), 他們分別表示返回所有的Promise和返回最快的那個。
對於Promise.all()來說,它會等待所有的Promise都執行完畢之後返回,如果其中有一個Promise被rejected,那麼整個Promise.all()都會被rejected。在這種情況下,如果有一個Promise被rejected,其他的Promise的結果也都獲取不了。
為了解決這個問題,ES11引入了Promise.allSettled() 方法,這個方法會等待所有的Promise結束,不管他們是否被rejected,所以可以使用下面的程式碼獲得所有的結果,而不管其中是否有Promise出現問題。
const promises = [promise1("/do1"), promise2("/do2")];
const allResults = await Promise.allSettled(promises);
const errors = results
.filter(p => p.status === 'rejected')
.map(p => p.reason);
??操作符
??操作符是一個判斷是否為空然後賦值的操作,如果沒有這個操作符,我們通常使用||來簡單的進行這個操作,如下所示:
const yourAge = someBody.age || 18
上面的程式碼意思是如果someBody.age 是空,那麼就將yourAge設定成為18。
但是上面程式碼有個問題,如果someBody.age=0 的話,上述邏輯也成立。使用??操作符可以解決這個問題。
const yourAge = someBody.age ?? 18
?.操作符
我們有時候在獲取某個物件的屬性的時候,需要進行物件的null判斷,否則從null物件中取出屬性就會報錯,但是通常的?:操作符使用起來太複雜了,如果有多個物件和屬性連寫的情況下更是如此,如果使用?.操作符就會簡單很多:
const age = school?.class?.student?.age;
如上所示,這個一個非常複雜的連寫操作,但是使用?.就變得很簡單。
同樣?.還可以用在物件的方法上:
const age = student.getAge?.();
上面程式碼表示,如果student的getAge方法存在,則呼叫,否則返回undefined。
總結
事實上所有的現代瀏覽器基本上都支援ES11了,IE除外。大家可以盡情嘗試ES11的新特徵。
本文已收錄於 http://www.flydean.com/ecmascript-11/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!