JavaScript ES2021 最值得期待的 5 個(gè)新特性解析

在寫(xiě)本文時(shí),本文提到的新的 JavaScript 提案功能已進(jìn)入第 4 階段,并且?guī)缀蹩隙〞?huì)包含在 ES2021 中。你已經(jīng)可以開(kāi)始在 最新版本的瀏覽器,Node.js 和 Babel 中使用。
注意:ECMAScript 是 JavaScript 所基于的標(biāo)準(zhǔn),由 TC39 委員會(huì)管理。ECMAScript 始終是一個(gè)不需要的名稱,這會(huì)使一切都對(duì)初學(xué)者感到困惑。人們經(jīng)常談?wù)?JavaScript 功能,但參考的是 ECMAScript 規(guī)范。
更新特性
數(shù)字分隔符( _)邏輯分配( &&=,||=,??=)引用不足( WeakRef和FinalizationRegistry)Promise.any String.prototype.replaceAll
1. 數(shù)值分隔符
大數(shù)字文字很難使人眼快速解析,尤其是當(dāng)有很多重復(fù)的數(shù)字時(shí):
1000000000000 1019436871.42
為了提高可讀性,新的 JavaScript 語(yǔ)言功能 啟用了下劃線作為數(shù)字文字中的分隔符。因此,上面的內(nèi)容現(xiàn)在可以重寫(xiě)為每千位數(shù)字,例如:
1_000_000_000_000 1_019_436_871.42
現(xiàn)在,更容易說(shuō)出第一個(gè)數(shù)字是 1 萬(wàn)億,而第二個(gè)數(shù)字大約是 10 億。
數(shù)字分隔符有助于提高各種數(shù)字文字的可讀性:
// A decimal integer literal with its digits grouped per thousand:
1_000_000_000_000
// A decimal literal with its digits grouped per thousand:
1_000_000.220_720
// A binary integer literal with its bits grouped per octet:
0b01010110_00111000
// A binary integer literal with its bits grouped per nibble:
0b0101_0110_0011_1000
// A hexadecimal integer literal with its digits grouped by byte:
0x40_76_38_6A_73
// A BigInt literal with its digits grouped per thousand:
4_642_473_943_484_686_707n
它們甚至適用于八進(jìn)制整數(shù)文字(盡管 我想不出 其中分隔符為此類文字提供值 的示例):
// A numeric separator in an octal integer literal: ???♀?
0o123_456
請(qǐng)注意,JavaScript 還具有不帶顯式 0o 前綴的八進(jìn)制文字的舊式語(yǔ)法。例如,017 === 0o17。在嚴(yán)格模式下或模塊內(nèi)不支持此語(yǔ)法,并且在現(xiàn)代代碼中不應(yīng)使用此語(yǔ)法。因此,這些文字不支持?jǐn)?shù)字分隔符。使用 0o17 風(fēng)格的文字代替。
2. Promise combinators
自從 ES2015 中引入 Promise 以來(lái),JavaScript 完全支持兩種 Promise 組合器:靜態(tài)方法 Promise.all 和 Promise.race。
目前有兩個(gè)新提案正在通過(guò)標(biāo)準(zhǔn)化流程:Promise.allSettled 和 Promise.any。有了這些添加,JavaScript 中將總共有四個(gè)諾言組合器,每個(gè)組合器支持不同的用例。
以下是這四個(gè)組合器的概述:

2.1 Promise.allSettled
Promise.allSettled 給你當(dāng)所有輸入的諾言是一種信號(hào)結(jié)算,這意味著他們要么履行或拒絕。如果您不在乎承諾的狀態(tài),而只是想知道工作何時(shí)完成,無(wú)論它是否成功,這都是很有用的。
例如,您可以啟動(dòng)一系列獨(dú)立的 API 調(diào)用,并使用 Promise.allSettled 它們來(lái)確保它們已全部完成,然后再執(zhí)行其他操作,例如刪除加載微調(diào)器:
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
await Promise.allSettled(promises);
// All API calls have finished (either failed or succeeded).
removeLoadingIndicator();
2.2 Promise.any
Promise.any 方法和 Promise.race 類似——只要給定的迭代中的一個(gè) promise 成功,就采用第一個(gè) promise 的值作為它的返回值,但與 Promise.race 的不同之處在于——它會(huì)等到所有 promise 都失敗之后,才返回失敗的值:
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
// Any of the promises was fulfilled.
console.log(first);
// → e.g. 'b'
} catch (error) {
// All of the promises were rejected.
console.assert(error instanceof AggregateError);
// Log the rejection values:
console.log(error.errors);
// → [
// <TypeError: Failed to fetch /endpoint-a>,
// <TypeError: Failed to fetch /endpoint-b>,
// <TypeError: Failed to fetch /endpoint-c>
// ]
}
此代碼示例檢查哪個(gè)端點(diǎn)響應(yīng)最快,然后將其記錄下來(lái)。只有當(dāng) 所有 請(qǐng)求都失敗時(shí),我們才最終進(jìn)入代碼 catch 塊,然后在其中處理錯(cuò)誤。
Promise.any 拒絕可以一次代表多個(gè)錯(cuò)誤。為了在語(yǔ)言級(jí)別支持此功能,引入了一種新的錯(cuò)誤類型,稱為 AggregateError。除了上面示例中的基本用法外,還可以以編程方式構(gòu)造 AggregateError 對(duì)象,就像其他錯(cuò)誤類型一樣:
const aggregateError = new AggregateError([errorA, errorB, errorC], 'Stuff went wrong!');
3. Weak references and finalizers
此功能包含兩個(gè)高級(jí)對(duì)象 WeakRef 和 FinalizationRegistry。根據(jù)使用情況,這些接口可以單獨(dú)使用,也可以一起使用。正確使用它們需要仔細(xì)考慮,如果可能,最好避免使用它們。
一般來(lái)說(shuō),在JavaScript中,對(duì)象的引用是強(qiáng)保留的,這意味著只要持有對(duì)象的引用,它就不會(huì)被垃圾回收。
const ref = { x: 42, y: 51 };
// 只要我們?cè)L問(wèn) ref 對(duì)象(或者任何其他引用指向該對(duì)象),這個(gè)對(duì)象就不會(huì)被垃圾回收
目前在 Javascript 中,WeakMap 和 WeakSet 是弱引用對(duì)象的唯一方法:將對(duì)象作為鍵添加到 WeakMap 或 WeakSet 中,是不會(huì)阻止它被垃圾回收的。
JavaScript 的 WeakMap 并不是真正意義上的弱引用:實(shí)際上,只要鍵仍然存活,它就強(qiáng)引用其內(nèi)容。WeakMap 僅在鍵被垃圾回收之后,才弱引用它的內(nèi)容。
WeakRef 是一個(gè)更高級(jí)的 API,它提供了真正的弱引用,Weakref 實(shí)例具有一個(gè)方法 deref,該方法返回被引用的原始對(duì)象,如果原始對(duì)象已被收集,則返回 undefined 對(duì)象。
JavaScript 中對(duì)象的引用是強(qiáng)引用,WeakMap 和 WeakSet 可以提供部分的弱引用功能,若想在 JavaScript 中實(shí)現(xiàn)真正的弱引用,可以通過(guò)配合使用 WeakRef 和終結(jié)器(Finalizer)來(lái)實(shí)現(xiàn)。
WeakRef 是用來(lái)指目標(biāo)對(duì)象不脫離垃圾收集保留它的對(duì)象。如果未通過(guò)垃圾回收回收目標(biāo)對(duì)象,則 WeakRefs 可以取消引用以允許訪問(wèn)目標(biāo)對(duì)象。
// Create a WeakRef object referring to a given target object
const ref = new WeakRef(targetObject)
// Return the WeakRef instance's target object, or undefined if the target object has been garbage-collected
const obj = ref.deref()
使用 FinalizationRegistry 對(duì)象可以在垃圾回收對(duì)象時(shí)請(qǐng)求回調(diào)。
// Create a registry object that uses the given callback
const registry = new FinalizationRegistry([callback])
// Register an object with a registry instance so that if the object is garbage-collected, the registry's callback may get called
registry.register(target, heldValue, [unregisterToken])
// Unregister a target object from a registry instance
registry.unregister(unregisterToken)
更多信息:TC39提案,V8
4. String.prototype.replaceAll
當(dāng)前,如果不使用全局正則表達(dá)式,就無(wú)法替換字符串中子字符串的所有實(shí)例。與字符串參數(shù)一起使用時(shí),String.prototype.replace 僅影響首次出現(xiàn)。
String.prototype.replaceAll() 將為開(kāi)發(fā)人員提供一種簡(jiǎn)單的方法來(lái)完成此常見(jiàn)的基本操作。
'aabbcc'.replaceAll('b', '.') // 'aa..cc'
'aabbcc'.replaceAll(/b/g, '.') // 'aa..cc'
5. Logical assignment (邏輯分配)
支持與新的運(yùn)營(yíng)邏輯分配 &&=,||= 和 ??=。與它們的 數(shù)學(xué)和按位對(duì)應(yīng)物不同,邏輯分配遵循其各自邏輯操作的短路行為。僅當(dāng)邏輯運(yùn)算將評(píng)估右側(cè)時(shí),它們才執(zhí)行分配。
// falsy: false, 0, -0, 0n, "", null, undefined, and NaN
// truthy: all values are truthy unless defined as falsy
// nullish: null or undefined
a ||= b
// Logical OR assignment
// Equivalent to: a || (a = b);
// Only assigns if a is falsy
a &&= b
// Logical AND assignment
// Equivalent to: a && (a = b);
// Only assigns if a is truthy
a ??= b
// Logical nullish assignment
// Equivalent to: a ?? (a = b);
// Only assigns if a is nullish
5.1 具體例子
帶有 && 運(yùn)算符的邏輯賦值運(yùn)算符
僅當(dāng) LHS 值為真時(shí),才將 RHS 變量值賦給 LHS 變量。
// Logical Assignment Operator with && operator
let num1 = 5
let num2 = 10
num1 &&= num2
console.log(num1) // 10
// Line 5 can also be written as following ways
// 1. num1 && (num1 = num2)
// 2. if (num1) num1 = num2
帶有 || 的運(yùn)算符邏輯賦值運(yùn)算符
僅當(dāng) LHS 值為假時(shí),才將 RHS 變量值賦給 LHS 變量。
// Logical Assignment Operator with || operator
let num1
let num2 = 10
num1 ||= num2
console.log(num1) // 10
// Line 5 can also be written as following ways
// 1. num1 || (num1 = num2)
// 2. if (!num1) num1 = num2
帶有 ?? 運(yùn)算符的邏輯賦值運(yùn)算符
ES2020 引入了空值合并運(yùn)算符,其也可以與賦值運(yùn)算符結(jié)合使用。僅當(dāng) LHS 為 undefined 或僅為 null 時(shí),才將 RHS 變量值賦給 LHS 變量。
// Logical Assignment Operator with ?? operator
let num1
let num2 = 10
num1 ??= num2
console.log(num1) // 10
num1 = false
num1 ??= num2
console.log(num1) // false
// Line 5 can also be written as following ways
// num1 ?? (num1 = num2)
概括

作為開(kāi)發(fā)人員,跟緊語(yǔ)言的新特性是很重要的。
以上將在 2021 年發(fā)布的一些新功能,它們是進(jìn)入第 4 階段的提案,幾乎可以肯定會(huì)包括在內(nèi),這些功能已經(jīng)在最新的瀏覽器和 babel 中實(shí)現(xiàn)。
參考文章:JavaScript Features in 2021
可以加貓哥的 wx:CB834301747 ,一起閑聊前端。
微信搜 “全棧修煉”,回復(fù) “電子書(shū)” 即可以獲得 160 本前端精華書(shū)籍哦。
往期精文
通過(guò)閱讀本篇文章,如果有收獲的話,可以點(diǎn)個(gè)贊和在看,這將會(huì)成為我持續(xù)分享的動(dòng)力,感謝~
