String.replace 默认只替换第一个匹配项,批量替换需用正则加g标志或循环调用;含元字符须转义,长词需前置排序;$等特殊字符在替换串中需转义或用函数参数避免误解析。

replace 不支持正则时的批量替换会静默失败
直接用 String.replace 处理多个不同旧词,默认只换第一个匹配项,其余全被忽略——这不是 bug,是设计行为。比如 "abc abc abc".replace("abc", "xyz") 结果是 "xyz abc abc",后续两个 "abc" 完全不动。
真正能批量替换的只有两种路径:用正则配合 g 标志,或循环调用 replace(不推荐)。关键看你的旧词是否含正则元字符(如 .、*、?),有就得先转义。
- 若所有旧词都是纯文本(无
.、^、$等),可用new RegExp(oldWords.join('|'), 'g') - 若旧词来源不可控(比如用户输入、配置文件),必须用转义函数处理每个词,否则
"file.txt".replace(/.txt/g, ".log")会误匹配任意字符 +"txt" - 大小写敏感需额外加
'i'标志,但注意replace返回新字符串,原字符串不变
用正则批量替换时,旧词顺序影响结果
当多个旧词存在包含关系(如 "user" 和 "username"),正则按 | 左到右顺序匹配。如果写成 /user|username/g,"username" 永远只会被当成 "user" 替换掉前半截。
解决办法是把长词放前面:/username|user/g。更稳妥的做法是按长度降序排列旧词数组再拼接:
const oldToNew = { username: 'account', user: 'person', admin: 'manager' };
const sortedKeys = Object.keys(oldToNew).sort((a, b) => b.length - a.length);
const regex = new RegExp(sortedKeys.map(k => k.replace(/[.*+?^${}()|[]\]/g, '\$&')).join('|'), 'g');注意 replace 的第二个参数可以是函数,这样能动态查表取新词:text.replace(regex, match => oldToNew[match])。
业务词汇含特殊字符(如 $、&)会导致替换出错
String.replace 对替换字符串里的 $ 符号有特殊解释:$& 表示整个匹配、$1 表示第一组捕获……如果新词里真有 "Price: $199",直接传进去会被当成引用捕获组,结果变成空或报错。
安全做法是用函数作为替换参数,或对新词做预处理:
- 函数方式最干净:
text.replace(regex, (match) => oldToNew[match].replace(/$/g, '$$$'))(三个$是replace的转义规则) - 或者改用
String.replaceAll(仅现代环境),它不解析$,但不支持正则,只能用于纯字符串替换 - 若必须用正则且新词固定,可提前把
$替换为$$$,&替换为&(仅在 HTML 场景下需要)
性能差异:1000 次小替换 vs 1 次大正则
对一段 10KB 文本做 50 个词的替换,用循环调用 replace 50 次,实际会扫描文本 50 遍;而一次正则只需扫描一遍,快 3–5 倍。但正则编译成本不可忽视——如果替换逻辑高频调用(如编辑器实时高亮),应缓存 RegExp 实例,而不是每次 new。
另外,V8 引擎对短文本 + 少量固定词的场景,replaceAll 可能比正则更快,因为跳过了正则解析开销。但只要涉及大小写忽略或复杂匹配逻辑,正则仍是唯一选择。
真正容易被忽略的是边界问题:比如想替换单词 "cat",但不想动 "scatter",这时必须加 单词边界,而 在字符串中要写成 \b,漏掉一个反斜杠就失效。

















