How do I replace all occurrences of a string?
技术背景
在 JavaScript 开发中,经常会遇到需要替换字符串中所有指定子字符串的需求。虽然现代浏览器支持 String.prototype.replaceAll()
方法,但在处理旧浏览器或特定场景时,我们需要了解更多实现方式。
实现步骤
现代浏览器使用 String.prototype.replaceAll()
从 2020 年 8 月起,现代浏览器开始支持 ECMAScript 2021 语言规范中定义的 String.prototype.replaceAll()
方法。
1 2
| let result = "1 abc 2 abc 3".replaceAll("abc", "xyz");
|
旧浏览器或不支持 replaceAll
的环境
正则表达式实现
1 2 3 4 5 6 7
| function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
function replaceAll(str, find, replace) { return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); }
|
拆分和拼接实现
1 2 3
| function replaceAll(str, find, replace) { return str.split(find).join(replace); }
|
性能考虑
在 Chrome Windows 8 机器上的测试表明,基于正则表达式的实现最快,拆分和拼接实现比正则表达式实现慢 53%。
处理特殊字符
正则表达式包含特殊(元)字符,因此在传递 find
参数时需要先进行转义处理,避免出现意外结果。
1 2 3
| function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
|
兼容性处理
replaceAll
填充函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if (!String.prototype.replaceAll) { Object.defineProperty(String.prototype, 'replaceAll', { configurable: true, writable: true, enumerable: false, value: function(search, replace) { return this.replace( Object.prototype.toString.call(search) === '[object RegExp]' ? search.global ? search : RegExp(search.source, /\/([a-z]*)$/.exec(search.toString())[1] + 'g') : RegExp(String(search).replace(/[.^$*+?()[{|\\]/g, "\\$&"), "g"), replace ); } }); }
|
核心代码
正则表达式替换
1 2 3 4 5 6 7
| function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
function replaceAll(str, find, replace) { return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); }
|
拆分和拼接替换
1 2 3
| function replaceAll(str, find, replace) { return str.split(find).join(replace); }
|
replaceAll
填充函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if (!String.prototype.replaceAll) { Object.defineProperty(String.prototype, 'replaceAll', { configurable: true, writable: true, enumerable: false, value: function(search, replace) { return this.replace( Object.prototype.toString.call(search) === '[object RegExp]' ? search.global ? search : RegExp(search.source, /\/([a-z]*)$/.exec(search.toString())[1] + 'g') : RegExp(String(search).replace(/[.^$*+?()[{|\\]/g, "\\$&"), "g"), replace ); } }); }
|
最佳实践
- 性能优先场景:如果需要处理大量字符串,建议使用基于正则表达式的实现。
- 兼容性优先场景:对于需要兼容旧浏览器的场景,可以使用
replaceAll
填充函数或拆分和拼接的实现。 - 避免意外替换:在使用正则表达式时,确保对特殊字符进行转义处理。
常见问题
正则表达式中的特殊字符
如果 search
参数包含正则表达式的特殊字符,需要先进行转义处理,否则可能会导致意外结果。
递归实现的性能问题
递归实现可能会导致性能问题,特别是处理长字符串时,可能会出现栈溢出错误。
旧浏览器兼容性问题
旧浏览器可能不支持 String.prototype.replaceAll()
方法,需要使用填充函数或其他替代方法。