ECMAScript 2024 正式發(fā)布,新特性一覽!
2024 年 6 月 26 日,第 127 屆 ECMA 大會(huì)正式批準(zhǔn)了 ECMAScript 2024 語(yǔ)言規(guī)范,這意味著它現(xiàn)在正式成為最新 ECMAScript 標(biāo)準(zhǔn)。
圖片
下面就來(lái)看看 ECMAScript 2024 都有哪些新特性吧!
- Promise.withResolvers()
 - Object.groupBy / Map.groupBy
 - String:isWellFormed() / toWellFormed()
 - ArrayBuffer:resize / transfer
 - Atomics.waitAsync()
 - 正則表達(dá)式 v 標(biāo)志
 
Promise.withResolvers()
Promise.withResolvers() 允許創(chuàng)建一個(gè)新的 Promise,并同時(shí)獲得 resolve 和 reject 函數(shù)。
Promise.withResolvers() 等同于以下代碼,不過(guò)代碼會(huì)更簡(jiǎn)潔:
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});通常,當(dāng)創(chuàng)建一個(gè)新的 Promise 時(shí),會(huì)傳遞一個(gè)執(zhí)行器函數(shù)給 Promise 構(gòu)造函數(shù),這個(gè)執(zhí)行器函數(shù)接收兩個(gè)參數(shù):resolve 和 reject 。但在某些情況下,可能想要在 Promise 創(chuàng)建之后仍然能夠訪問(wèn)到這兩個(gè)函數(shù)。這就是 Promise.withResolvers() 的用武之地。
使用 Promise.withResolvers() 的例子:
const { promise, resolve, reject } = Promise.withResolvers();  
  
// 在這里可以使用 resolve 和 reject 函數(shù)  
setTimeout(() => resolve('成功!'), 8000);  
  
promise.then(value => {  
  console.log(value); // 輸出: 成功!  
});使用 Promise.withResolvers() 關(guān)鍵的區(qū)別在于resolve 和 reject函數(shù)現(xiàn)在與 Promise 本身處于同一作用域,而不是在執(zhí)行器中被創(chuàng)建和一次性使用。這可能使得一些更高級(jí)的用例成為可能,例如在重復(fù)事件中重用它們,特別是在處理流和隊(duì)列時(shí)。這通常也意味著相比在執(zhí)行器內(nèi)包裝大量邏輯,嵌套會(huì)更少。這個(gè)方法對(duì)于那些需要更細(xì)粒度控制 Promise 的狀態(tài),或者在 Promise 創(chuàng)建后仍然需要訪問(wèn) resolve 和 reject 函數(shù)的場(chǎng)景來(lái)說(shuō)非常有用。
瀏覽器支持:
圖片
Object.groupBy() / Map.groupBy()
Object.groupBy() 和 Map.groupBy() 方法用于數(shù)組分組。
假設(shè)有一個(gè)由表示水果的對(duì)象組成的數(shù)組,需要按照顏色進(jìn)行分組。以前可以使用forEach循環(huán)來(lái)實(shí)現(xiàn),代碼如下:
const fruits = [  
  { name: "Apple", color: "red" },  
  { name: "Banana", color: "yellow" },  
  { name: "Cherry", color: "red" },  
  { name: "Lemon", color: "yellow" },  
  { name: "Grape", color: "purple" },  
];
const fruitsByColor = {};  
fruits.forEach(fruit => {  
  const color = fruit.color;  
  if (!fruitsByColor[color]) {  
    fruitsByColor[color] = [];  
  }  
  fruitsByColor[color].push(fruit);  
}); 
console.log(fruitsByColor);輸出結(jié)果如下:
圖片
也可以使用reduce方法:
const fruitsByColor = fruits.reduce((acc, fruit) => {  
  const color = fruit.color;  
  if (!acc[color]) {  
    acc[color] = [];  
  }  
  acc[color].push(fruit);  
  return acc;  
}, {});無(wú)論哪種方式,代碼都略顯繁瑣。每次都要檢查對(duì)象,看分組的 key 是否存在,如果不存在,則創(chuàng)建一個(gè)空數(shù)組,并將項(xiàng)目添加到該數(shù)組中。
Object.groupBy()
可以通過(guò)以下方式來(lái)使用新的Object.groupBy方法,代碼更簡(jiǎn)潔:
const fruitsByColor = Object.groupBy(fruits, (fruit) => fruit.color);需要注意,使用Object.groupBy方法返回一個(gè)沒(méi)有原型(即沒(méi)有繼承任何屬性和方法)的對(duì)象。這意味著該對(duì)象不會(huì)繼承Object.prototype上的任何屬性或方法,例如hasOwnProperty或toString等。雖然這樣做可以避免意外覆蓋Object.prototype上的屬性,但也意味著不能使用一些與對(duì)象相關(guān)的方法。
const fruitsByColor = Object.groupBy(fruits, (fruit) => fruit.color);
console.log(fruitsByColor.hasOwnProperty("red"));
// TypeError: fruitsByColor.hasOwnProperty is not a function在調(diào)用Object.groupBy時(shí),傳遞給它的回調(diào)函數(shù)應(yīng)該返回一個(gè)字符串或 Symbol 類(lèi)型的值。如果回調(diào)函數(shù)返回其他類(lèi)型的值,它將被強(qiáng)制轉(zhuǎn)換為字符串。
瀏覽器支持:
圖片
Map.groupBy()
Map.groupBy和Object.groupBy的功能一樣,只是返回的結(jié)果類(lèi)型不同。Map.groupBy返回一個(gè) Map 對(duì)象,而Object.groupBy返回一個(gè)普通對(duì)象。
const fruits = [  
  { name: "Apple", color: "red" },  
  { name: "Banana", color: "yellow" },  
  { name: "Cherry", color: "red" },  
  { name: "Lemon", color: "yellow" },  
  { name: "Grape", color: "purple" },  
];
const fruitsByColor = Map.groupBy(fruits, (fruit) => fruits.color);這里根據(jù)水果顏色進(jìn)行了分組,輸出結(jié)果如下:
圖片
可以通過(guò) Map 的 get 方法來(lái)來(lái)獲取分組分組結(jié)果:
fruitsByColor.get("red");
// [{"name": "Apple", "color": "red"}, {"name": "Cherry", "color": "red"}]瀏覽器支持:
String:isWellFormed() / toWellFormed()
String.prototype.isWellFormed()
isWellFormed() 用于檢查一個(gè) UTF-16 編碼的字符串是否包含孤立的代理項(xiàng)(即未與另一個(gè)代理項(xiàng)配對(duì)的高代理或低代理)。UTF-16使用代理對(duì)來(lái)表示Unicode中超過(guò)基本多文種平面的字符。一個(gè)有效的UTF-16字符串應(yīng)該只包含正確配對(duì)的代理對(duì)或單獨(dú)的BMP字符。
下面來(lái)看一個(gè)例子
const strings = [
  // 單獨(dú)的前導(dǎo)代理
  "ab\uD800",
  "ab\uD800c",
  // 單獨(dú)的后尾代理
  "\uDFFFab",
  "c\uDFFFab",
  // 格式正確
  "abc",
  "ab\uD83D\uDE04c",
];
for (const str of strings) {
  console.log(str.isWellFormed());
}
// 輸出:
// false
// false
// false
// false
// true
// true如果傳遞的字符串格式不正確, encodeURI 會(huì)拋出錯(cuò)誤。可以通過(guò)使用 isWellFormed() 在將字符串傳遞給 encodeURI() 之前測(cè)試字符串來(lái)避免這種情況。
const illFormed = "https://example.com/search?q=\uD800";
try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e); // URIError: URI malformed
}
if (illFormed.isWellFormed()) {
  console.log(encodeURI(illFormed));
} else {
  console.warn("Ill-formed strings encountered."); // Ill-formed strings encountered.
}isWellFormed() 函數(shù)的使用場(chǎng)景主要包括以下幾種情況:
- 數(shù)據(jù)驗(yàn)證:當(dāng)你從外部源(如用戶輸入、文件、網(wǎng)絡(luò)請(qǐng)求等)接收字符串時(shí),你可能想要驗(yàn)證這些字符串是否包含有效的UTF-16編碼。如果字符串包含孤立的代理項(xiàng)(即沒(méi)有配對(duì)的高代理或低代理),那么它可能不是有效的UTF-16字符串,這可能會(huì)導(dǎo)致后續(xù)處理時(shí)出錯(cuò)。
 - 文本處理:在處理文本數(shù)據(jù)(如搜索、排序、轉(zhuǎn)換等)時(shí),確保文本是有效編碼的非常重要。如果文本包含錯(cuò)誤的編碼,那么處理結(jié)果可能會(huì)是不正確的或不可預(yù)測(cè)的。
 - 網(wǎng)絡(luò)傳輸:當(dāng)你通過(guò)網(wǎng)絡(luò)發(fā)送或接收文本數(shù)據(jù)時(shí),確保數(shù)據(jù)的編碼是正確的至關(guān)重要。錯(cuò)誤的編碼可能導(dǎo)致數(shù)據(jù)在傳輸過(guò)程中被損壞,或者在接收端無(wú)法正確解析。
 - 數(shù)據(jù)庫(kù)存儲(chǔ):在將文本數(shù)據(jù)存儲(chǔ)到數(shù)據(jù)庫(kù)之前,驗(yàn)證其編碼的正確性也是一個(gè)好習(xí)慣。這可以確保數(shù)據(jù)的完整性和可讀性,并避免在后續(xù)查詢或處理時(shí)出現(xiàn)問(wèn)題。
 - 用戶界面顯示:在用戶界面中顯示文本時(shí),確保文本是有效編碼的也很重要。錯(cuò)誤的編碼可能導(dǎo)致文本無(wú)法正確顯示,或者顯示出不正確的字符。
 
瀏覽器支持:
圖片
String.prototype.toWellFormed()
toWellFormed() 方法返回一個(gè)字符串,其中該字符串的所有單獨(dú)代理項(xiàng)都被替換為 Unicode 替換字符 U+FFFD。
toWellFormed() 迭代字符串的碼元,并將任何單獨(dú)代理項(xiàng)替換為 Unicode 替換字符 U+FFFD。這確保了返回的字符串格式正確并可用于期望正確格式字符串的函數(shù),比如 encodeURI。由于引擎能夠直接訪問(wèn)字符串的內(nèi)部表示,與自定義實(shí)現(xiàn)相比 toWellFormed() 更高效。
const strings = [
  // 單獨(dú)的前導(dǎo)代理
  "ab\uD800",
  "ab\uD800c",
  // 單獨(dú)的后尾代理
  "\uDFFFab",
  "c\uDFFFab",
  // 格式正確
  "abc",
  "ab\uD83D\uDE04c",
];
for (const str of strings) {
  console.log(str.toWellFormed());
}
// 輸出:
// "ab?"
// "ab?c"
// "?ab"
// "c?ab"
// "abc"
// "ab??c"如果傳遞的字符串格式不正確, encodeURI 會(huì)拋出錯(cuò)誤??梢韵韧ㄟ^(guò)使用 toWellFormed() 將字符串轉(zhuǎn)換為格式正確的字符串來(lái)避免這種情況。
const illFormed = "https://example.com/search?q=\uD800";
try {
  encodeURI(illFormed);
} catch (e) {
  console.log(e); // URIError: URI malformed
}
console.log(encodeURI(illFormed.toWellFormed())); // "https://example.com/search?q=%EF%BF%BD"瀏覽器支持:
圖片
ArrayBuffer:resize / transfer
ArrayBuffer.prototype.resize
ArrayBuffer 實(shí)例的 resize() 方法將 ArrayBuffer 調(diào)整為指定的大小,以字節(jié)為單位,前提是該 ArrayBuffer 是可調(diào)整大小的并且新的大小小于或等于該 ArrayBuffer 的 maxByteLength。
const buffer = new ArrayBuffer(8, { maxByteLength: 16 });
console.log(buffer.byteLength); // 8
if (buffer.resizable) {
  console.log("緩沖區(qū)大小是可調(diào)整的!");
  buffer.resize(12);
}注意:
- 如果 ArrayBuffer 已分離或不可調(diào)整大小,則拋出該錯(cuò)誤。
 - 如果 newLength 大于該 ArrayBuffer 的 maxByteLength,則拋出該錯(cuò)誤。
 
瀏覽器支持:
ArrayBuffer.prototype.transfer
transfer() 方法執(zhí)行與結(jié)構(gòu)化克隆算法相同的操作。它將當(dāng)前 ArrayBuffer 的字節(jié)復(fù)制到一個(gè)新的 ArrayBuffer 對(duì)象中,然后分離當(dāng)前 ArrayBuffer 對(duì)象,保留了當(dāng)前 ArrayBuffer 的大小可調(diào)整性。
// 創(chuàng)建一個(gè) ArrayBuffer 并寫(xiě)入一些字節(jié)
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[1] = 2;
view[7] = 4;
// 將緩沖區(qū)復(fù)制到另一個(gè)相同大小的緩沖區(qū)
const buffer2 = buffer.transfer();
console.log(buffer.detached); // true
console.log(buffer2.byteLength); // 8
const view2 = new Uint8Array(buffer2);
console.log(view2[1]); // 2
console.log(view2[7]); // 4
// 將緩沖區(qū)復(fù)制到一個(gè)更小的緩沖區(qū)
const buffer3 = buffer2.transfer(4);
console.log(buffer3.byteLength); // 4
const view3 = new Uint8Array(buffer3);
console.log(view3[1]); // 2
console.log(view3[7]); // undefined
// 將緩沖區(qū)復(fù)制到一個(gè)更大的緩沖區(qū)
const buffer4 = buffer3.transfer(8);
console.log(buffer4.byteLength); // 8
const view4 = new Uint8Array(buffer4);
console.log(view4[1]); // 2
console.log(view4[7]); // 0
// 已經(jīng)分離,拋出 TypeError
buffer.transfer(); // TypeError: Cannot perform ArrayBuffer.prototype.transfer on a detached ArrayBuffer瀏覽器支持:
圖片
Atomics.waitAsync()
Atomics.waitAsync() 靜態(tài)方法異步等待共享內(nèi)存的特定位置并返回一個(gè) Promise。與 Atomics.wait() 不同,waitAsync 是非阻塞的且可用于主線程。
下面來(lái)看一個(gè)簡(jiǎn)單的例子,給定一個(gè)共享的 Int32Array。
const sab = new SharedArrayBuffer(1024);
const int32 = new Int32Array(sab);令一個(gè)讀取線程休眠并在位置 0 處等待,預(yù)期該位置的值為 0。result.value 將是一個(gè) promise。
const result = Atomics.waitAsync(int32, 0, 0, 1000);
// { async: true, value: Promise {<pending>} }在該讀取線程或另一個(gè)線程中,對(duì)內(nèi)存位置 0 調(diào)用以令該 promise 解決為 "ok"。
Atomics.notify(int32, 0);
// { async: true, value: Promise {<fulfilled>: 'ok'} }如果它沒(méi)有解決為 "ok",則共享內(nèi)存該位置的值不符合預(yù)期(value 將是 "not-equal" 而不是一個(gè) promise)或已經(jīng)超時(shí)(該 promise 將解決為 "time-out")。
瀏覽器支持:
圖片
正則表達(dá)式 v 標(biāo)志
RegExp 的 v 標(biāo)志是 u 標(biāo)志的超集,并提供了另外兩個(gè)功能:
- 字符串的 Unicode 屬性:通過(guò) Unicode 屬性轉(zhuǎn)義,可以使用字符串的屬性。
 
const re = /^\p{RGI_Emoji}$/v;
// 匹配僅包含 1 個(gè)代碼點(diǎn)的表情符號(hào):
re.test('?'); // '\u26BD'  // true
// 匹配由多個(gè)代碼點(diǎn)組成的表情符號(hào):
re.test('??????'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F' // true- 設(shè)置符號(hào):允許在字符類(lèi)之間進(jìn)行集合操作。
 
const re = /[\p{White_Space}&&\p{ASCII}]/v;
re.test('\n'); // true
re.test('\u2028'); // false那 u 標(biāo)志是干什么的呢?
在 JavaScript 中,RegExp 對(duì)象的 u 標(biāo)志(也稱(chēng)為 Unicode 標(biāo)志)是用于處理 Unicode 字符的。當(dāng)在正則表達(dá)式字面量或構(gòu)造函數(shù)中使用 u 標(biāo)志時(shí),它將改變正則表達(dá)式的行為,以便更準(zhǔn)確地處理 Unicode 字符。以下是 u 標(biāo)志的主要作用:
- 完整 Unicode 字符匹配: 在 Unicode 中,有些字符是由多個(gè)“代碼單元”(在 UTF-16 編碼中)組成的,如表情符號(hào)或某些特殊字符。不使用 u 標(biāo)志時(shí),正則表達(dá)式可能會(huì)將這些字符拆分成多個(gè)代碼單元,導(dǎo)致不正確的匹配。使用 u 標(biāo)志后,正則表達(dá)式會(huì)將整個(gè)字符視為一個(gè)單位,從而進(jìn)行更準(zhǔn)確的匹配。
 - Unicode 屬性的支持: 使用 u 標(biāo)志的正則表達(dá)式可以訪問(wèn) Unicode 字符的屬性,如是否為大寫(xiě)字母、是否為標(biāo)點(diǎn)符號(hào)等。這可以通過(guò)在正則表達(dá)式中使用 \p{...} 和 \P{...} 序列來(lái)實(shí)現(xiàn),其中 {...} 中是 Unicode 屬性的名稱(chēng)。
 - Unicode 字符類(lèi)別的支持: 與 Unicode 屬性類(lèi)似,使用 u 標(biāo)志的正則表達(dá)式還可以支持 Unicode 字符類(lèi)別,如 \p{Letter} 匹配任何字母字符,\p{Number} 匹配任何數(shù)字字符等。
 - 點(diǎn)號(hào)(.)的新行為: 在默認(rèn)情況下,正則表達(dá)式中的點(diǎn)號(hào)(.)不匹配換行符,但會(huì)匹配任何單個(gè)字符(在 UTF-16 編碼中是一個(gè)代碼單元)。但是,當(dāng)使用 u 標(biāo)志時(shí),點(diǎn)號(hào)將匹配任何單個(gè) Unicode 字符,包括那些由多個(gè) UTF-16 代碼單元組成的字符。
 - 對(duì)量詞和邊界斷言的改進(jìn): 使用 u 標(biāo)志的正則表達(dá)式將改進(jìn)對(duì) Unicode 字符的量詞(如 *, +, ?, {n}, {n,}, {n,m})和邊界斷言(如 ^ 和 $)的處理。這確保了它們?cè)谡麄€(gè) Unicode 字符上正確工作,而不是僅僅在 UTF-16 代碼單元上。
 - 更準(zhǔn)確的字符類(lèi): 使用 u 標(biāo)志的正則表達(dá)式將更準(zhǔn)確地解釋字符類(lèi),如 [a-z]。在不使用 u 標(biāo)志時(shí),這個(gè)字符類(lèi)可能只匹配基本的拉丁字母。但是,使用 u 標(biāo)志后,它將根據(jù) Unicode 標(biāo)準(zhǔn)來(lái)匹配任何被視為“小寫(xiě)字母”的字符,包括來(lái)自其他語(yǔ)言的小寫(xiě)字母。
 
下面是一個(gè)使用 u 標(biāo)志的例子:
const regex = /^\p{Letter}+$/u;  
console.log(regex.test('你好')); // 輸出:true  
console.log(regex.test('123')); // 輸出:false在這個(gè)例子中,正則表達(dá)式 \p{Letter}+ 匹配一個(gè)或多個(gè) Unicode 字母字符。由于 '你好' 包含兩個(gè) Unicode 字母字符(即使它們?cè)?UTF-16 編碼中由多個(gè)代碼單元組成),所以 regex.test('你好') 返回 true。而 '123' 不包含任何 Unicode 字母字符,所以 regex.test('123') 返回 false。















 
 
 






 
 
 
 