String.prototype.replaceAll

發佈於 · 標籤為 ECMAScript ES2021

如果您曾經處理過 JavaScript 中的字串,您很有可能會遇到 String#replace 方法。String.prototype.replace(searchValue, replacement) 會傳回一個字串,其中某些符合條件的字串會根據您指定的參數進行替換

'abc'.replace('b', '_');
// → 'a_c'

'🍏🍋🍊🍓'.replace('🍏', '🥭');
// → '🥭🍋🍊🍓'

一個常見的用例是替換給定子字串的所有實例。但是,String#replace 並未直接處理此用例。當 searchValue 為字串時,只會替換子字串的第一個出現位置

'aabbcc'.replace('b', '_');
// → 'aa_bcc'

'🍏🍏🍋🍋🍊🍊🍓🍓'.replace('🍏', '🥭');
// → '🥭🍏🍋🍋🍊🍊🍓🍓'

為了解決這個問題,開發人員通常會將搜尋字串轉換成帶有全域 (g) 旗標的正規表示法。這樣一來,String#replace 就能替換所有符合條件的字串

'aabbcc'.replace(/b/g, '_');
// → 'aa__cc'

'🍏🍏🍋🍋🍊🍊🍓🍓'.replace(/🍏/g, '🥭');
// → '🥭🥭🍋🍋🍊🍊🍓🍓'

對於開發人員來說,如果您真正想要的只是一個全域子字串替換,卻必須進行這種字串轉正規表示法的轉換,是一件很惱人的事。更重要的是,這種轉換容易出錯,而且是常見的錯誤來源!請考慮以下範例

const queryString = 'q=query+string+parameters';

queryString.replace('+', ' ');
// → 'q=query string+parameters' ❌
// Only the first occurrence gets replaced.

queryString.replace(/+/, ' ');
// → SyntaxError: invalid regular expression ❌
// As it turns out, `+` is a special character within regexp patterns.

queryString.replace(/\+/, ' ');
// → 'q=query string+parameters' ❌
// Escaping special regexp characters makes the regexp valid, but
// this still only replaces the first occurrence of `+` in the string.

queryString.replace(/\+/g, ' ');
// → 'q=query string parameters' ✅
// Escaping special regexp characters AND using the `g` flag makes it work.

將字串文字(例如 '+')轉換成全域正規表示法不只是移除 ' 引號、用 / 斜線將其包起來,然後加上 g 旗標而已 — 我們必須對正規表示法中具有特殊意義的任何字元進行跳脫。這很容易忘記,而且很難做對,因為 JavaScript 沒有提供內建機制來跳脫正規表示法模式。

另一個解決方法是將 String#splitArray#join 結合使用

const queryString = 'q=query+string+parameters';
queryString.split('+').join(' ');
// → 'q=query string parameters'

這種方法可以避免任何跳脫,但會產生將字串分割成陣列部分的開銷,而且最後還要再將其黏合在一起。

顯然,這些解決方法都不是理想的。如果 JavaScript 中的基本作業(例如全域子字串替換)可以更直接了當,那不是很好嗎?

String.prototype.replaceAll #

新的 String#replaceAll 方法解決了這些問題,並提供了一個直接的機制來執行全域子字串替換

'aabbcc'.replaceAll('b', '_');
// → 'aa__cc'

'🍏🍏🍋🍋🍊🍊🍓🍓'.replaceAll('🍏', '🥭');
// → '🥭🥭🍋🍋🍊🍊🍓🍓'

const queryString = 'q=query+string+parameters';
queryString.replaceAll('+', ' ');
// → 'q=query string parameters'

為了與語言中現有的 API 保持一致,String.prototype.replaceAll(searchValue, replacement) 的行為與 String.prototype.replace(searchValue, replacement) 完全相同,但有以下兩個例外:

  1. 如果 searchValue 是字串,則 String#replace 只會替換子字串的第一個出現位置,而 String#replaceAll 則會替換所有出現位置。
  2. 如果 searchValue 是非全域的 RegExp,則 String#replace 只會替換單一符合條件的字串,類似於它對待字串的方式。另一方面,String#replaceAll 在這種情況下會擲回例外,因為這很可能是個錯誤:如果您真的想要「替換所有」符合條件的字串,您應該使用全域正規表示法;如果您只想替換單一符合條件的字串,可以使用 String#replace

新功能的重要部分在於第一個項目。String.prototype.replaceAll 豐富了 JavaScript,提供了對全域子字串替換的一流支援,無需正規表示式或其他解決方法。

關於特殊替換模式的說明 #

值得注意的是:replacereplaceAll 都支援 特殊替換模式。雖然這些模式與正規表示式搭配使用時最有用,但其中一些模式($$$&$`$')在執行簡單字串替換時也會生效,這可能會令人驚訝

'xyz'.replaceAll('y', '$$');
// → 'x$z' (not 'x$$z')

如果您的替換字串包含這些模式之一,而且您想按原樣使用它們,您可以選擇退出神奇替換行為,改用回傳字串的替換函式

'xyz'.replaceAll('y', () => '$$');
// → 'x$$z'

String.prototype.replaceAll 支援 #