ES15 (2024)中5個令人驚嘆的新JavaScript特性
2024年:又是一個帶來全新JS特性升級的不可思議的年份,ES15推出。
從復(fù)雜的異步特性到語法糖數(shù)組和現(xiàn)代正則表達式,JavaScript編碼現(xiàn)在比以往任何時候都更簡單、更快捷。
1.原生數(shù)組分組終于到來
Object.groupBy():
const fruits = [
{ name: 'pineapple??', color: '??' },
{ name: 'apple??', color: '??' },
{ name: 'banana??', color: '??' },
{ name: 'strawberry??', color: '??' },
];
const groupedByColor = Object.groupBy(
fruits,
(fruit, index) => fruit.color
);
// 原生 group by 示例
console.log(groupedByColor);
圖片
字面意思就是讓恐龍級的 Lodash 庫失去了最后的存在理由 - 再也不需要了!
圖片
我原本期待一個新的實例方法,比如Array.prototype.groupBy,但不知什么原因他們把它做成了靜態(tài)方法。
然后我們還有Map.groupBy來用對象鍵進行分組:
const array = [1, 2, 3, 4, 5];
const odd = { odd: true };
const even = { even: true };
Map.groupBy(array, (num, index) => {
return num % 2 === 0 ? even : odd;
});
// => Map { {odd: true}: [1, 3, 5], {even: true}: [2, 4] }
不過幾乎沒人會這樣對數(shù)組分組,所以可能不會那么受歡迎。
2.從外部解決promise - 現(xiàn)代方式
使用Promise.withResolvers()。
從外部解決promises是很普遍的需求,在此之前我們不得不使用Deferred類來實現(xiàn):
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
const deferred = new Deferred();
deferred.resolve();
或者從NPM安裝 - 又多了一個依賴!
圖片
但現(xiàn)在有了ES15的Promise.withResolvers():
const { promise, resolve, reject } = Promise.withResolvers();
看看我如何用它來快速地將事件流promise化 - await一個observable:
// data-fetcher.js
const { promise, resolve, reject } = Promise.withResolvers();
function startListening() {
eventStream.on('data', (data) => {
resolve(data);
});
}
async function getData() {
return await promise;
}
// client.js
const { startListening, getData } = require('./data-fetcher.js');
startListening();
// ? 監(jiān)聽單個流事件
const data = await getData();
3. Buffer性能升級
Buffers是用來存儲應(yīng)用程序生成的臨時數(shù)據(jù)的小型數(shù)據(jù)存儲。
它們使得在管道的各個階段之間傳輸和處理數(shù)據(jù)變得非常容易。
像這樣的管道:
- 文件處理: 輸入文件 → buffer → 處理 → 新buffer → 輸出文件
- 視頻流: 網(wǎng)絡(luò)響應(yīng) → buffer → 顯示視頻幀
- 餐廳隊列: 接待顧客 → 隊列/buffer → 服務(wù)顧客
const fs = require('fs');
const { Transform } = require('stream');
const inputFile = 'input.txt';
const outputFile = 'output.txt';
const inputStream = fs.createReadStream(inputFile, 'utf-8');
const transformStream = new Transform({
transform(chunk) {
// ? 從緩沖區(qū)轉(zhuǎn)換塊
},
});
const outputStream = fs.createWriteStream(outputFile);
// ? 開始管道
inputStream.pipe(transformStream).pipe(outputStream);
使用 buffers,每個階段可以以不同的速度獨立處理數(shù)據(jù)。
但是當(dāng)通過管道移動的數(shù)據(jù)超過buffer容量時會發(fā)生什么?
以前我們必須將當(dāng)前所有數(shù)據(jù)的buffer復(fù)制到一個更大的buffer中。
這對性能來說很糟糕,尤其是當(dāng)管道中將有大量數(shù)據(jù)時。
ES15為我們提供了解決這個問題的方案:可調(diào)整大小的數(shù)組buffers。
const resizableBuffer = new ArrayBuffer(1024, {
maxByteLength: 1024 ** 2,
});
// ? 調(diào)整大小到 2048 字節(jié)
resizableBuffer.resize(1024 * 2);
4.異步升級
Atomics.waitAsync(): ES2024中另一個強大的異步編碼特性:
它是當(dāng)2個代理共享一個buffer時...
代理1"睡眠"并等待代理2完成任務(wù)。
當(dāng)代理2完成時,它使用共享buffer作為通道進行通知。
const sharedBuffer = new SharedArrayBuffer(4096);
const bufferLocation = new Int32Array(sharedBuffer);
// 初始化緩沖區(qū)位置的初始值
bufferLocation[37] = 0x1330;
async function doStuff() {
// ? agent 1:在共享緩沖區(qū)位置等待直到通知
Atomics.waitAsync(bufferLocation, 37, 0x1330).then(
(r) => { /* 處理到達 */ }
);
}
function asyncTask() {
// ? agent 2:在共享緩沖區(qū)位置通知
const bufferLocation = new Int32Array(sharedBuffer);
Atomics.notify(bufferLocation, 37);
}
如果你認(rèn)為這類似于普通的async/await,你絕對是對的。
但最大的區(qū)別是:這2個代理可以存在于完全不同的代碼上下文中 - 它們只需要訪問相同的buffer。
而且:多個代理可以在不同時間訪問或等待共享buffer - 其中任何一個都可以通知"喚醒"所有其他代理。
這就像P2P網(wǎng)絡(luò);而async/await更像是客戶端-服務(wù)器請求-響應(yīng)模式。
const sharedBuffer = new SharedArrayBuffer(4096);
const bufferLocation = new Int32Array(sharedBuffer);
bufferLocation[37] = 0x1330;
// ? 從 postMessage() 接收到的共享緩沖區(qū)
const code = `
var ia = null;
onmessage = function (ev) {
if (!ia) {
postMessage("Aux worker is running");
ia = new Int32Array(ev.data);
}
postMessage("Aux worker is sleeping for a little bit");
setTimeout(function () { postMessage("Aux worker is waking"); Atomics.notify(ia, 37); }, 1000);
};`;
async function doStuff() {
// ? agent 1:存在于 Worker 上下文中
const worker = new Worker(
'data:application/javascript,' + encodeURIComponent(code)
);
worker.onmessage = (event) => {
// 記錄事件
};
worker.postMessage(sharedBuffer);
Atomics.waitAsync(bufferLocation, 37, 0x1330).then(
(r) => { /* 處理到達 */ }
);
}
function asyncTask() {
// ? agent 2:在共享緩沖區(qū)位置通知
const bufferLocation = new Int32Array(sharedBuffer);
Atomics.notify(bufferLocation, 37);
}
5.正則表達式v標(biāo)志和集合操作
這是一個全新的特性,使正則表達式更加清晰和直觀。
使用表達式模式查找和操作復(fù)雜字符串 - 在集合操作的幫助下:
// A 和 B 是字符類,如 [a-z]
// 差異:匹配 A 但不匹配 B
[A--B]
// 交集:同時匹配 A 和 B
[A&&B]
// 嵌套字符類
[A--[0-9]]
匹配不斷增加的Unicode字符集,如:
- 表情符號: ??, ??, ??, ??, 等
- 重音字母: é, à, ?, ?, 等
- 符號和非拉丁字符: ?, ?, €, £, μ, ¥, 等
所以這里我們使用Unicode正則表達式和v標(biāo)志來匹配所有希臘字母:
const regex = /[\p{Script_Extensinotallow=Greek}&&\p{Letter}]/v;
最后的想法
總的來說,ES15對JavaScript來說是一個重大飛躍,包含了幾個對現(xiàn)代開發(fā)至關(guān)重要的特性。幫助你以更簡潔、更富表現(xiàn)力、更清晰的方式編寫更干凈的代碼。