該來的還是來了,盤點 ES12 中有新特性!
今天主要介紹一下 ECMAScript 2021(ES12)的一部分的 JS 功能。
- 邏輯賦值操作符
- 數(shù)字分隔符(1_000)
- Promise.any 與 AggregateError
- String.prototype.replaceAll
- WeakRefs 與 FinalizationRegistry 對象
邏輯賦值操作符
邏輯賦值操作符將邏輯操作(&&、||或??)與賦值表達(dá)式組合在一起。
- x ||= y;
- x || (x = y);
- x &&= y;
- x && (x = y);
- x ??= y;
- x ?? (x = y);
帶有&&的邏輯賦值操作符
- let x = 1;
- let y = 2;
- x &&= y;
- console.log(x); // 2
x &&= y 等價于 x && (x = y)。
或者等價于
- if(x) {
- x = y
- }
因為x是一個真值,所以它被賦值為y,即2。
帶有||的邏輯賦值操作符
- let x = 1;
- let y = 2;
- x ||= y;
- console.log(x); // 1
x ||= y 等價于 x || (x = y)。
這意味著賦值操作只在x為虛值時才會發(fā)生。在我們的代碼中,x包含1,這是一個真值,因此,賦值不會發(fā)生。這就是我們的代碼在控制臺中打印1的原因。
簡單地說
- const updateID = user => {
- // 我們可以這樣做
- if (!user.id) user.id = 1
- // 或者這樣
- user.id = user.id || 1
- // 或者這樣
- user.id ||= 1
- }
帶有??的邏輯賦值操作符
?? 在 JS 中專門檢查一個值是否為 null 或undefined。
- let a;
- let b = a ?? 5;
- console.log(b); // 5
在第二行,let b = a ?? 5,如果a的值為null 或undefined,??求值并賦值給b。
現(xiàn)在考慮?? 和==。
- let x;
- let y = 2;
- x ??= y;
- console.log(x); // 2
x ??= y 等價于 x = x ?? (x=y)
數(shù)字分隔符
它允許我們在數(shù)字之間添加下劃線(_)字符,使數(shù)字更具可讀性。
例如
- const num = 100000000
被0的數(shù)量所迷惑
分隔符解決這個問題:
- const num = 100_000_000
分隔符可以用于數(shù)字的整數(shù)部分和小數(shù)部分。
- const num = 1_000_000.123_456
分隔符不僅可以用在整數(shù)和浮點數(shù)中,也可以用在二進(jìn)制、十六進(jìn)制、八進(jìn)制字面量中。
分隔符也適用于BigInt數(shù)字。
- const trillion = 1000_000_000_000n;
- console.log(trillion.toString()); // "1000000000000"
分隔符只是為了可讀性。所以,它可以放在數(shù)字內(nèi)的任何地方。
- const amount = 178_00; // 00 after _ for cents.
Promise.any 與 AggregateError
Promise.any()返回第一個完成的promise的值。如果所有傳遞給Promise.any()作為參數(shù)(作為數(shù)組)的Promise都被拒絕,則拋出一個"AggregateError"異常。
AggregateError`是一個新的Error子類,它對單個錯誤進(jìn)行分組。每個AggregateError實例都包含一個對異常數(shù)組的引用。
考慮下面例子:
下面我們有3個 promise,它們是隨機的。
- const p1 = new Promise((resolve, reject) => {
- setTimeout(() => resolve("A"), Math.floor(Math.random() * 1000));
- });
- const p2 = new Promise((resolve, reject) => {
- setTimeout(() => resolve("B"), Math.floor(Math.random() * 1000));
- });
- const p3 = new Promise((resolve, reject) => {
- setTimeout(() => resolve("C"), Math.floor(Math.random() * 1000));
- });
在p1, p2和p3中,最先的完成的的由Promise.any()執(zhí)行。
- (async function() {
- const result = await Promise.any([p1, p2, p3]);
- console.log(result); // 打印 "A", "B" 或者 "C"
- })();
如果所有的 promise 都失敗了?在這種情況下,Promise.any()拋出AggregateError異常。我們需要捕獲它:
- const p = new Promise((resolve, reject) => reject());
- try {
- (async function() {
- const result = await Promise.any([p]);
- console.log(result);
- })();
- } catch(error) {
- console.log(error.errors);
為了演示的目的,在Promise.any()中我們只能它一個 promise。而這個 promise 是失敗的。上述代碼在控制臺中記錄了以下錯誤。
String.prototype.replaceAll 方法
String.prototype.replaceAll()允許我們用一個不同的值替換字符串中的所有子串實例,而不需要使用全局正則表達(dá)式。
目前,JavaScript字符串有一個replace()方法。它可以用來用另一個字符串替換一個字符串。
- const str = "Backbencher sits at the Back";
- const newStr = str.replace("Back", "Front");
- console.log(newStr); // "Frontbencher sits at the Back"
如果輸入模式是一個字符串,replace()方法只替換第一次出現(xiàn)的內(nèi)容。這就是為什么在代碼中,"Back"的第二次出現(xiàn)沒有被替換。
只有將模式作為正則表達(dá)式提供時,才能進(jìn)行完全替換。
- const str = "Backbencher sits at the Back";
- const newStr = str.replace(/Back/g, "Front");
- console.log(newStr); // "Frontbencher sits at the Front"
我們來看另一個例子
- const strWithPlus = '++'
- const strWithComma = strWithPlus.replace(/+/g, ', ')
- // , ,
這種方法需要使用正則表達(dá)式。然而,復(fù)雜的正則表達(dá)式往往是錯誤的來源。(沒有人喜歡RegEx 😬)
還有另一種方法是使用String.prototype.split()和Array.prototype.join()方法
- const strWithPlus = '++'
- const strWithComma = strWithPlus.split('+').join(', ')
- // , ,
這種方法避免使用正則表達(dá)式,但是必須將字符串拆分為單獨的部分(單詞),將其轉(zhuǎn)換為一個數(shù)組,然后將數(shù)組元素連接為一個新字符串。
string.prototype.replaceAll()解決了這些問題,并為全局替換子串提供了簡單而方便的方式:
- const strWithPlus = '++'
- const strWithComma = strWithPlus.replaceAll('+', ', ')
- // , ,
注意:如果使用全局正則表達(dá)式作為查找值,那么replace和replaceAll的行為是一樣的。
WeakRefs 與 FinalizationRegistry 對象
WeakRef 是弱引用的意思。弱引用的主要用途是實現(xiàn)大型對象的緩存或映射。在這種情況下,我們不希望長期保留大量的內(nèi)存來保存這種很少使用的緩存或映射。我們可以讓內(nèi)存很快被垃圾回收,以后如果我們再次需要它,我們可以生成一個新的緩存。
JS 是會自動垃圾收集。如果一個變量不再可達(dá),JS 垃圾收集器將自動刪除它。你可以在MDN中閱讀更多關(guān)于 JS 垃圾收集的內(nèi)容。
WeaseRefs(弱引用)提供了兩個新功能:
- 使用WeakRef類創(chuàng)建對對象的弱引用
- 使用FinalizationRegistry類在垃圾收集之后運行自定義收集器
簡而言之,WeakRef允許我們創(chuàng)建對象的弱引用,這些對象是另一個對象的屬性值,而finalizers可以用來,除其他外,移除對被垃圾收集器 "清理"過的對象的引用。
在創(chuàng)建使用內(nèi)置緩存的記憶化(memoization)函數(shù)時,如果緩存中存在傳遞給函數(shù)的參數(shù)的計算值,這種技術(shù)可能很有用(前提是對象被用作緩存對象的屬性值,以及它們隨后被刪除的風(fēng)險),以防止重復(fù)執(zhí)行函數(shù)。
在構(gòu)建內(nèi)聯(lián)緩存時
- 如果沒有內(nèi)存泄漏的風(fēng)險,那么使用 Map
- 當(dāng)使用可以隨后刪除對象的鍵時,使用 WeakMap
- 當(dāng)使用可以隨后刪除的值對象時,請將Map與WeakRef結(jié)合使用
提案中最后一個例子:
- function makeWeakCached(f) {
- const cache = new Map()
- return key => {
- const ref = cache.get(key)
- if (ref) {
- //
- const cached = ref.deref()
- if (cached !== undefined) return cached;
- }
- const fresh = f(key)
- // ( )
- cache.set(key, new WeakRef(fresh))
- return fresh
- };
- }
- const getImageCached = makeWeakCached(getImage);
- WeakRef構(gòu)造函數(shù)接受一個參數(shù),該參數(shù)必須是一個對象,并返回對該對象的弱引用
- WeakRef 實例的deref方法返回兩個值中的一個。
在內(nèi)置緩存的情況下,finalizer被設(shè)計為在一個值對象被垃圾收集器銷毀后完成清理過程,或者更簡單地說,刪除對這樣一個對象的弱引用。
- function makeWeakCached(f) {
- const cache = new Map()
- // -
- const cleanup = new FinalizationRegistry(key => {
- const ref = cache.get(key)
- if (ref && !ref.deref()) cache.delete(key)
- })
- return key => {
- const ref = cache.get(key)
- if (ref) {
- const cached = ref.deref()
- if (cached !== undefined) return cached
- }
- const fresh = f(key)
- cache.set(key, new WeakRef(fresh))
- // ( )
- cleanup.register(fresh, key)
- return fresh
- }
- }
- const getImageCached = makeWeakCached(getImage);
作者:KUMAR HARSH
譯者:前端小智 來源:blog 原文:https://dev.to/cenacr007_harsh/es2021-features-3pa