身為開發人員,掌握 JavaScript 的最新動態,是讓程式碼更高效、現代且具有延展性的關鍵。
在這篇文章中,我們將一起探索 ES2020 到 ES2023 令人激動不已的 JavaScript 新特性,這在許多年前是難以想像的,這些理念不僅能啟發您,也能將開發過程帶來新的光景。
ES2020
Optional Chaining (?.)
ES2020 中引入的 Optional Chaining 可讓您讀取位於物件屬性鏈深處的值,而無需檢查鏈中的每個屬性是否存在。
let name = person?.address?.street?.name;
Nullish Coalescing (??)
ES2020 中也引入了空值合併運算符,如果一個表達式不為 null 或 undefined,則傳回第一個結果,否則傳回後面的表達式。
const person = {};
let name = person?.name ?? 'Unknown';
邏輯賦值操作符
以下操作符在 ES2020 皆可用
let x = 10;
x &&= 5; // 如果第一個值是true,則指派第二個值。
x ||= 5; // 如果第一個值是false,則指派第二個值。
x ??= 5; // 如果第一個值是undefined或null,則指派第二個值。
BigInt
BigInt 是 ES2020 中的一種新的基本型別,用於表示任意精確度的整數,從而允許使用大整數進行精確計算。
const x = 12345678901234567890n;
const y = 98765432123456789012n;
console.log(x + y);
延伸資源,如果您想要對原生 BigInt 進行一系列數學運算,請參考 bigint-toolkit
GlobalThis
新的全域物件 globalThis
提供了一種與現代 JavaScript 環境相容的存取全域物件的方法。
這個變數將傳統瀏覽器與 Node 環境各自使用的 window
, this
, global
等全域 root 物件集合在一起。
下面示範了在瀏覽器中, globalThis === window
console.log(globalThis === window); // true in a browser
matchAll()
String
原型物件上有一個新新方法 matchAll()
,可傳回一個迭代器,該迭代器產生正規表示式與字串的匹配,包括捕獲群組。
const regex = /(\w)(\d)/g;
const str = 'a1b2c3';
for (const match of str.matchAll(regex)) {
console.log(match);
}
Promise.allSettled()
Promise API 上的一個新方法 allSettled()
非常實用,他會傳回一個新 Promise,當陣列中的所有子 Promises 都處理完成後(無論 resolve 或 reject),這個 Promise 就得到 resolve 狀態。
const promises = [
Promise.resolve('a'),
Promise.reject('b'),
Promise.resolve('c')
];
Promise.allSettled(promises)
.then((results) => console.log(results));
Dynamic import
JavaScript 中的動態匯入可讓您選擇非同步的匯入 JS 檔案,這就像我們長年在 Webpack 和 Babel 所做的那樣。
此功能將幫助您交付按需請求的程式碼,即程式碼分割,而無需 webpack 或其他模組捆綁器的消耗。如果您願意,也可以用 if-else 區分要載入的程式碼。
if (foo === bar) {
import('./Baz').then((Baz) => {
console.log(Baz.Baz);
});
}
ES2021
Promise.any()
任何一個 Promise 成功就返回
Promise.any([promise1, promise2]).then((x) => {
myDisplay(x);
});
如果所有的 Promise 都失敗,則 Promise 背拒絕且丟出 AggregateError,也就是找到的所有錯誤的集合。
Promise.any(promises).then((value) => {
console.log(value);
}).catch((err) => { console.log("error: " + err) });
你可以用 AggregateError.errors
取得所有 Error 物件
String.replaceAll()
replaceAll
方法會將所有符合的字串替換為傳入的替換值。pattern
參數可以是字串或正則表達式模式,而 replacement
參數則可以是字串或函式。
const message = 'hello+good+morning';
const messageWithSpace = message.replaceAll('+', ' ');
// hello good morning
WeakRef and Finalizers
WeakRef
弱引用在其他語言已經很流行,它可以幫助我們建立快到更大物件的映射。由於 WeakRef 變數是對物件的唯一引用,因此當瀏覽器進行垃圾收集時,JavaScript 引擎可以安全地將其從記憶體中刪除並釋放空間。
const weakRef = new WeakRef({
age: 13;
});
console.log(weakRef.deref().age)
// output: 13
WeakRef 和 Finalizers 通常會一起使用。FinalizationRegistry 方法讓你可以在物件被垃圾回收機制回收後請求執行一個回呼函式。
首先,你需要定義一個帶有回呼函式的 registry(註冊表)。接著,使用 .register
方法把你想觀察的物件註冊到這個 registry。這樣一來,當物件的記憶體被垃圾回收機制釋放時,你就會立即收到通知。
const registry = new FinalizationRegistry(value => {
console.log("Finalizer");
});
registry.register({object: "Do Something"}, "testObject");
Numeric separators
這項新功能可讓您使用下劃線作為數字文字的分隔符號。它透過視覺上分隔數字分組來提高數字的可讀性。
// A billion
const amount = 1000000000; // Previous
const amount = 1_000_000_000; // With ES2021
// Hundreds of millions
const amount = 1475938.38; // Previous
const amount = 1_475_938.38; // With ES2021
Private Accessors
Private Accessors 的運作方式與 Private Methods 非常相似,我們現在可以在 JavaScript 中定義私有的 getter 和 setter,使它們只能在類別內部或透過建立的實例存取。
class ExampleClass {
get #Greeting() {return "Hello Good Morning !"
}
get viewGreetingMsg() {
return this.#Greeting
}
}
let exampleClass = new ExampleClass();
console.log(exampleClass.viewGreetingMsg);
// Hello Good Morning !
Intl.DateTimeFormat 增加 style 選項
Intl.DateTimeFormat
可以啟用依語言調整的日期和時間格式**。**dateStyle
和 timeStyle
`選項允許我們請求給定長度的特定於區域設定的日期和時間。
// Time with short format let.
let time = new Intl.DateTimeFormat('en' , { timeStyle: 'short' });
console.log(time .format(Date.now())); // 09:27 PM
// Time with medium format.
let time = new Intl.DateTimeFormat('en' , { timeStyle: 'medium'});
console.log(time .format(Date.now())); //09:27:50 PM
// Time with long format.
let time = new Intl.DateTimeFormat('en' , { timeStyle: 'long' }) ;
console.log(time .format(Date.now())); // 11:27:50 PM GMT+11
ES2022
正規表示式匹配索引
JS 的 Regex 匹配功能,在某些情況下,您可能不僅需要匹配的 index,還需要每個捕獲的子字串的開始和結束索引。
透過這個新功能,您可以使用修飾符: d
來表示您想要指定符合字串的起始索引和結束索引。請參閱以下範例。
const animals = "Animals: Brown Bear, Koala, Polar Bear"
// with /d flag
const regex2 = /(Bear)/dg
console.log(...animals.matchAll(regex2))
// Output:
// [
// 'Bear',
// 'Bear',
// index: 15,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined,
// indices: [ [15, 19], [15, 19], groups: undefined, length: 2 ]
// ] [
// 'Bear',
// 'Bear',
// index: 34,
// input: "Animals: Brown Bear, Koala, Polar Bear",
// groups: undefined,
// indices: [ [34, 38], [34, 38], groups: undefined, length: 2 ]// ]
Top-level await
儘管 async await 在 JavaScript 中已經使用了一段時間,但在 ES2022 之前,await僅在 async 範圍內可用。
在 ES2022 中,您可以在最外層的空間使用 await,並為您先前處理過的同步問題提供解決方案。例如,await會暫停模組及其父模組的渲染,直到匯入資源並確保事物得到正確協調。但這項特性導致他無法被 Polyfill 用在舊瀏覽器上。
// At top level of a JS file
const translationKeys = await import(/api/${language} );
const connection = await dbConnector();
String.prototype.at()
String 原型上的新方法 at()
傳回指定位置的字串,也可以接受負數值來取得字串末端起算的字元。
const str = 'hello';
console.log(str.at(0)); // 'h'
console.log(str.at(-1)); // 'o'
Error cause
也是非常實用的功能,Error
物件擁有新屬性 cause
允許您指定錯誤的造成原因。終於可以在 catch 裡面根據錯誤原因做不同的流程判斷了。
try {
throw new Error('Error occurred', { cause: new Error('Underlying cause') });
} catch (error) {
console.log(error.cause);
}
Object.hasOwn()
Object.hasOwn()
是 hasOwnProperty()
方法的替代方法。儘管Object.prototype.hasOwnProperty已經出現在 JavaScript 規格中相當長一段時間了,但它有一些缺點,例如可以被覆蓋而造成失效,以及無法處理 Object.create(null)
建立出來的物件
let person = {
hasOwnProperty: function() {
return false;
},
age: 35
};
console.log(Object.hasOwn(person, 'age')); // true
console.log(person.hasOwnProperty('age')); // false
類別私有屬性
現在 class 可以擁有私有屬性,私有訪法,無論是動態還是靜態。也可以用 in
判斷私有欄位是否存在物件中。
class User {
#userName;
static #role;
static #login() {
this.#userName = "Hermione Granger";
}
static isLogin(user){
return #userName in user && #login in user
}
}
ES2023
從陣列最後搜尋
findLast()
與 findLastIndex()
函數將允許我們根據條件查找數組的最後一個到第一個元素。
const array = [{a: 1, b: 1}, {a: 2, b: 2}, {a: 3, b: 3}, {a: 4, b: 4}]
console.log(array.findLast(n => n)); //result -> {a: 4,b: 4 }
console.log(array.findLast(n => n.a * 5 === 20)); // result -> {a:4,b:4} as the condition is true so it returns the last element.
console.log(array.findLast(n => n.a * 5 === 21)); //result -> undefined as the condition is false so return undefined instead of {a:4,b:4}.
console.log(array.findLastIndex(n => n.a * 5 === 21)); // result -> -1 as the condition is not justified for returning the last element.
console.log(array.findLastIndex(n => n.a * 5 === 20)); // result -> 3 which is the index of the last element as the condition is true.
Hashbang 語法
此功能使我們能夠在某些 CLI 中使用 Hashbang / Shebang。類似於常用的 Bash 腳本。
#!
是腳本開頭的一個特殊指令,它告訴作業系統在執行腳本時使用哪個編譯器。
#!/usr/bin/env node
console.log(2*3);
Symbol 可作為 WeakMap 的 Key
這功能允許使用唯一的 Symbol 符號作為 key。目前 WeakMap 僅限於允許物件作為 key,之後就會方便許多。
const weak = new WeakMap();
const key = Symbol('my ref');
const someObject = { a:1 };
weak.set(key, someObject);
console.log(weak.get(key));
Immutable 陣列修改方法
Array.prototype
提供了一系列新的修改功能,但都會傳回新陣列,而不是修改原有陣列。
新引進的功能有:
Array.prototype.toReversed()
Array.prototype.toSorted(compareFn)
Array.prototype.toSpliced(start, deleteCount, ...items)
Array.prototype.with(index, value)
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
/* toReversed */
const reversed = numbers.toReversed();
console.log("reversed", reversed); // "reversed", [9, 8, 7, 6, 5, 4, 3, 2, 1]
console.log("original", numbers); // "original", [1, 2, 3, 4, 5, 6, 7, 8, 9]
/* toSorted */
const sortedArr = numbers.toSorted();
console.log("sorted", sortedArr); // "sorted", [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log("original", numbers); // "original", [1, 2, 3, 4, 5, 6, 7, 8, 9]
/* with */
const replaceWith = numbers.with(1, 100);
console.log("with", replaceWith); // "with", [1, 100, 3, 4, 5, 6, 7, 8, 9]
console.log("original", numbers); // "original", [1, 2, 3, 4, 5, 6, 7, 8, 9]
/* toSpliced */
const splicedArr = numbers.toSpliced(0, 4);
console.log("toSpliced", splicedArr); // "toSpliced", [5, 6, 7, 8, 9]
console.log("original", numbers); // "original", [1, 2, 3, 4, 5, 6, 7, 8, 9]
結論
JavaScript 的語法逐漸在進化,透過這些新穎且強大的語法,我們能夠提高程式碼的效率與可讀性,以解放產能帶來更多創造性與可能性。
除了這些特性以外,今年推出的 ES2024 (ES15) 也加入了非常多令人驚豔並期待已久的功能,請參考我們另一篇文章: