如果您曾经在 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#split
与 Array#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)
完全相同,但有以下两个例外。
- 如果
searchValue
是一个字符串,那么String#replace
只替换子字符串的第一个出现,而String#replaceAll
替换所有出现。 - 如果
searchValue
是一个非全局正则表达式,那么String#replace
只替换单个匹配项,类似于它对字符串的行为。另一方面,String#replaceAll
在这种情况下会抛出异常,因为这可能是一个错误:如果您确实想“替换所有”匹配项,则可以使用全局正则表达式;如果您只想替换单个匹配项,则可以使用String#replace
。
新的功能的关键部分在于第一项。String.prototype.replaceAll
为 JavaScript 提供了对全局子字符串替换的一流支持,无需使用正则表达式或其他解决方法。
关于特殊替换模式的说明 #
值得一提的是:replace
和 replaceAll
都支持 特殊替换模式。虽然这些模式在与正则表达式结合使用时最有用,但其中一些模式 ($$
、$&
、$`
和 $'
) 在执行简单字符串替换时也会生效,这可能会让人感到意外。
'xyz'.replaceAll('y', '$$');
// → 'x$z' (not 'x$$z')
如果您的替换字符串包含其中一个模式,并且您想按原样使用它们,您可以选择退出神奇的替换行为,方法是使用返回字符串的替换函数。
'xyz'.replaceAll('y', () => '$$');
// → 'x$$z'
String.prototype.replaceAll
支持 #
- Chrome: 从版本 85 开始支持
- Firefox: 从版本 77 开始支持
- Safari: 从版本 13.1 开始支持
- Node.js: 从版本 16 开始支持
- Babel: 支持