
js replace 怎么用:字符串替换的基本写法与常见场景
JavaScript 的 replace 用于根据指定的查找内容生成一个替换后的字符串。它常见于修正文案、清理输入、格式化文本和处理匹配结果。掌握它的关键不只是记住“查找什么、换成什么”,还要弄清查找参数是字符串还是正则、替换是否只发生一次,以及替换内容中的特殊标记如何解释。
本文中的示例可以直接放进浏览器控制台或 Node.js 运行。需要特别记住:replace 不会直接修改原字符串;要使用结果,必须接收它的返回值。
一、基本语法与返回值
const result = text.replace(searchValue, replacement);
text 是待处理的字符串;searchValue 是要查找的内容,可以是字符串,也可以是正则表达式;replacement 是替换字符串或替换函数。调用结果是一个新字符串,原来的 text 不会改变。
const text = "早上好,世界!";
const result = text.replace("世界", "JavaScript");
console.log(result); // 早上好,JavaScript!
console.log(text); // 早上好,世界!
如果没有找到匹配内容,通常返回与原字符串内容相同的新结果,不会因为没有匹配而报错。替换内容是空字符串时,可以用来删除匹配部分:
const fileName = "report-draft.txt";
const result = fileName.replace("-draft", "");
console.log(result); // report.txt
字符串的 replace 方法不会改变原字符串,因为 JavaScript 字符串是不可变值。下面的写法只调用了方法,却丢弃了返回值:
let title = "旧标题";
title.replace("旧", "新");
console.log(title); // 旧标题
如果希望后续使用替换结果,应重新赋值:
let title = "旧标题";
title = title.replace("旧", "新");
console.log(title); // 新标题
二、查找参数是普通字符串时
把普通字符串传给 replace 时,它会查找与该字符串完全相同的内容。字符串中的正则元字符,例如点号、星号和括号,在这种情况下都按普通字符处理,不需要转义。
const text = "版本 1.2.3";
const result = text.replace(".", "-");
console.log(result); // 版本 1-2.3
注意,上例只替换了第一个点号。对普通字符串查找,replace 只替换第一次匹配;字符串参数没有可以添加的 g 标志。若要替换所有相同文本,优先考虑 replaceAll:
const text = "猫、狗、猫";
const result = text.replaceAll("猫", "兔");
console.log(result); // 兔、狗、兔
replaceAll 是较新的 JavaScript 字符串方法,现代浏览器和较新的 Node.js 均支持。若运行环境不支持它,可使用带 g 标志的正则表达式,或根据项目的兼容性要求选择其他方案:
const text = "猫、狗、猫";
const result = text.replace(/猫/g, "兔");
console.log(result); // 兔、狗、兔
当查找内容来自用户输入、需要按字面意思查找时,不要轻易把它直接拼接进正则表达式。正则里的点号、方括号等有特殊意义,输入若包含这些字符,可能造成意料之外的匹配;某些复杂模式还可能带来性能风险。简单的字面文本替换通常直接使用字符串参数或 replaceAll 更合适。
三、使用正则表达式替换
传入正则表达式时,可以用正则语法描述一类匹配内容,例如连续空白、数字或特定格式的片段。正则表达式后面的标志会影响替换行为。
没有 g 标志时,只替换第一个匹配;带 g 标志时,替换所有匹配:
const text = "红 红 红";
console.log(text.replace(/红/, "蓝")); // 蓝 红 红
console.log(text.replace(/红/g, "蓝")); // 蓝 蓝 蓝
i 标志表示忽略大小写,g 表示全局匹配。标志可以组合使用:
const text = "JavaScript javascript JAVASCRIPT";
const result = text.replace(/javascript/gi, "JS");
console.log(result); // JS JS JS
如果将正则表达式传给 replaceAll,该正则必须带有 g 标志,否则会抛出 TypeError。这是 replaceAll 与 replace 使用正则时的一个重要区别:
const text = "a-a-a";
console.log(text.replaceAll(/a/g, "b")); // b-b-b
// 会抛出 TypeError:正则没有 g 标志
// text.replaceAll(/a/, "b");
一个常见用途是折叠重复空白。使用正则时,\s 可匹配空格、制表符和换行符等空白字符,+ 表示匹配一个或多个:
const text = " 多个 空格
和换行 ";
const result = text.replace(/\s+/g, " ").trim();
console.log(result); // 多个 空格 和换行
这里的 trim 负责移除结果首尾的空白,正则负责把连续空白折叠成一个普通空格。需要保留换行或制表符的场合,不要不加判断地这样处理。
四、替换字符串中的特殊标记
当第二个参数是字符串时,其中部分美元符号序列有特殊含义。它们不是普通文本,而是引用匹配结果或捕获组:
$$:插入一个字面量美元符号$。$&:插入本次完整匹配的内容。$`:插入本次匹配之前的原字符串内容。$':插入本次匹配之后的原字符串内容。$1、$2等:插入对应编号的捕获组内容。$<name>:插入名称为name的命名捕获组内容。
例如,使用 $& 在匹配到的编号两侧添加括号:
const text = "订单编号:A123";
const result = text.replace(/[A-Z]\d+/g, "[$&]");
console.log(result); // 订单编号:[A123]
通过捕获组重新排列文本时,可以使用 $1、$2 等编号。捕获组按照左括号在正则中的出现顺序编号:
const date = "2025-03-08";
const result = date.replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1");
console.log(result); // 03/08/2025
命名捕获组适合结构较复杂、希望代码更易读的情况。命名组在正则中写作 (?<名称>...),替换字符串中用 $<名称> 引用:
const date = "2025-03-08";
const result = date.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
"$<month>/$<day>/$<year>"
);
console.log(result); // 03/08/2025
只有在正则表达式提供了相应捕获组时,编号或命名引用才有意义。若替换字符串中出现美元符号,而目标是输出字面上的 $& 或 $1,要特别留意它们会被解释为替换标记。使用 $$ 可以表示一个字面量美元符号。
五、使用替换函数进行条件处理
当替换结果需要根据匹配内容计算时,可以把函数作为第二个参数。每次匹配都会调用一次函数,函数的返回值会作为该次匹配的替换内容。这比在替换字符串里拼接复杂规则更清楚,也能避免替换字符串中的美元标记被当作特殊语法解析。
例如,将文本中的数字加一:
const text = "第 3 章,共 12 章";
const result = text.replace(/\d+/g, (match) => {
return String(Number(match) + 1);
});
console.log(result); // 第 4 章,共 13 章
回调函数收到的参数取决于正则表达式中的捕获组。通常,参数依次为:完整匹配、各捕获组内容、匹配起始位置、原字符串;如果使用了命名捕获组,最后还会传入一个包含命名组的对象。下面的例子展示了编号捕获组和匹配位置:
const text = "商品 12";
const result = text.replace(/(\d+)/, (match, digits, offset, input) => {
console.log(match); // 12
console.log(digits); // 12
console.log(offset); // 3
console.log(input); // 商品 12
return String(Number(digits) * 2);
});
console.log(result); // 商品 24
如果正则包含多个捕获组,回调参数中会按组的先后顺序依次出现。捕获组较多或使用命名组时,可以接收剩余参数,再区分末尾的偏移量、原字符串和命名组对象;不要假设所有回调场景都只有固定的四个参数。
命名组对象可以从回调参数末尾取得:
const result = "2025-03-08".replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
(match, year, month, day, offset, input, groups) => {
return `${groups.month}/${groups.day}/${groups.year}`;
}
);
console.log(result); // 03/08/2025
回调函数直接返回文本,不会再把返回值中的 $& 或 $1 当作替换字符串标记展开。例如,下面的写法会按字面插入美元符号和字符:
const result = "金额".replace("金额", () => "$&");
console.log(result); // $&
因此,当替换文本包含用户提供的内容或字面美元符号时,回调函数往往比构造替换字符串更直观。不过,它本身并不会自动清理或验证返回内容。
六、常见使用场景
1. 删除或改写固定标记
要删除明确的固定片段,可以把替换内容设为空字符串。若只想删掉一个位置,用 replace;若要删除所有出现的位置,可用 replaceAll 或全局正则。
const text = "临时-文件-临时";
const result = text.replaceAll("临时-", "");
console.log(result); // 文件-临时
2. 格式化电话号码或编号
在已确定输入格式的前提下,可用捕获组重排字符。下面的例子针对恰好十位数字的北美电话号码格式,不是通用的国际号码解析器:
const phone = "4155550132";
const result = phone.replace(/^(\d{3})(\d{3})(\d{4})$/, "($1) $2-$3");
console.log(result); // (415) 555-0132
如果输入可能包含国家区号、分机号、空格或其他格式,应先明确支持范围并做校验,不能仅靠一个替换表达式假定所有电话号码都遵循相同规则。
3. 处理文件名中的后缀
可以用正则只替换末尾的扩展名。下面的写法将 .txt 或 .md 改为 .html:
const fileName = "guide.md";
const result = fileName.replace(/\.(txt|md)$/i, ".html");
console.log(result); // guide.html
这里的 $ 是正则中的字符串末尾锚点,不是替换字符串中的特殊标记。两者处于不同位置,含义不同。
4. 对匹配内容做条件替换
回调函数可根据匹配值执行不同逻辑,例如把三位数用方括号包起来,而其他文本保持不变:
const text = "7、42、128";
const result = text.replace(/\d+/g, (match) => {
return match.length === 3 ? `[${match}]` : match;
});
console.log(result); // 7、42、[128]
5. 将简单日期格式转换为展示格式
若输入已经验证为固定的 年-月-日 格式,可以用捕获组调整显示顺序。此类字符串重排不等同于日期合法性校验:例如,替换本身不会判断月份和日期是否真实有效。
const date = "2025-03-08";
const result = date.replace(/^(\d{4})-(\d{2})-(\d{2})$/, "$2/$3/$1");
console.log(result); // 03/08/2025
七、关键边界与常见错误
1. 误以为普通字符串查找会替换所有匹配
replace("a", "b") 只替换第一次出现的字面字符串。要替换全部字面匹配,使用 replaceAll("a", "b");要使用正则规则并全局替换,使用带 g 的正则。
2. 忘记给正则加 g
/词/ 只替换第一个匹配,/词/g 才会替换全部匹配。需要全部替换时,检查正则标志,而不是只看表达式主体。
3. 把正则符号当成普通字符,或反过来
作为字符串参数传入的 "." 会查找字面点号;作为正则的 /./ 中,点号通常表示除行终止符外的任意单个字符。若要在正则中匹配字面点号,应写成 /\./。
const text = "a.b.c";
console.log(text.replace(".", "-")); // a-b.c
console.log(text.replace(/\./g, "-")); // a-b-c
4. 忽略替换字符串的特殊语法
$&、$1 和 $<name> 等在字符串替换参数中可能有特殊含义。若目标是插入字面文本,先判断是否需要用 $$ 转义美元符号,或改用回调函数返回内容。
5. 混淆正则锚点与替换标记
正则里的 ^ 和 $ 常用于约束匹配位置;替换字符串里的 $& 和 $1 则用于引用匹配内容。它们看起来都带美元符号,但含义由所在位置决定。
6. 正则表达式包含状态标志时不做验证
带 g 或 y 标志的正则具有 lastIndex 状态属性。某些反复调用场景中,若同一个正则还被其他正则 API 使用,容易因为状态变化而造成难以理解的结果。遇到这种情况,检查正则是否在循环或多个调用之间共享,并用小测试确认预期行为;不要把带状态正则的调用当作完全无状态的字符串查找。
7. 把文本替换当作完整校验
正则替换可以改写匹配到的字符,却不会自动验证业务规则。例如,把日期分隔符改成斜杠,不代表输入是合法日期;移除非数字字符,也不代表剩余数字满足账号、电话号码或金额的规则。校验应针对明确的数据格式另行实现。
八、Unicode 与空字符串查找
JavaScript 字符串使用 UTF-16 表示。一个可见字符不一定对应一个 UTF-16 码元;某些扩展字符会由一对码元表示。一般的字面替换和正则替换应根据实际文本测试,尤其是使用点号、量词或字符位置时,不要简单地把 JavaScript 字符串长度理解为用户看到的字符数量。
在较新的 JavaScript 环境中,replaceAll("", "-") 会在字符串的边界及相邻位置之间插入替换内容。例如:
console.log("abc".replaceAll("", "-")); // -a-b-c-
空字符串查找通常不是实际需求。由于 JavaScript 字符串按 UTF-16 表示,涉及代理对等扩展字符时,按位置插入可能产生不符合用户直觉的结果。若要按用户感知的字素簇处理字符(例如由多个码点组成的组合字符或表情序列),应使用专门的 Unicode 文本处理方案,而不是把空字符串替换当作逐个可见字符遍历。
九、替换与安全:它不等于转义或消毒
replace 只是文本处理方法,不会自动让内容变安全。把字符串中的某些字符简单替换掉,不能普遍防止跨站脚本、SQL 注入或路径问题;安全处理必须结合数据最终进入的上下文。
- HTML 页面文本:将不可信内容放入 HTML 时,应使用与 HTML 上下文对应的可靠转义方式。若只是把文本显示在网页元素中,通常优先用 DOM 的
textContent,不要把不可信字符串直接拼进innerHTML。 - HTML 属性、JavaScript、CSS 和 URL:它们是不同的上下文,所需的编码和验证规则也不同。对 HTML 文本做一次替换,不代表内容在这些上下文中都安全。
- SQL:不要通过字符串拼接和
replace过滤输入来防止 SQL 注入。使用数据库驱动提供的参数化查询。 - 用户提供的正则:如果把用户输入当作正则执行,应使用符合项目环境的正则转义方法,明确限制模式和输入长度,并评估复杂正则导致拒绝服务的风险。若只需查找字面文本,直接传字符串更稳妥。
例如,下面的替换并不是可靠的 HTML 安全方案:
// 不要把这种简单替换当作通用 HTML 转义
const unsafe = userInput.replace("<", "<");
它只处理了部分情况,也没有根据输出上下文做完整编码。若是在页面中以文本方式呈现内容,可使用 DOM API:
const element = document.querySelector("#message");
element.textContent = userInput;
如果确实需要允许部分 HTML 格式,应使用经过维护的 HTML 清理方案,并按应用需求配置允许的标签和属性。不要把“用 replace 替换几个危险字符”当作 HTML 清理器。
十、如何选择 replace、replaceAll 和正则
- 只替换一个明确的字面字符串:使用
replace("目标", "新内容")。 - 替换所有明确的字面字符串:使用
replaceAll("目标", "新内容")。 - 需要匹配一类内容,且只改第一个匹配:使用不带
g的正则。 - 需要对一类内容执行全局替换:使用带
g的正则。 - 替换内容需要计算、判断或引用外部数据:使用回调函数。
- 替换字符串里需要捕获组重排:可用
$1、命名组标记,或改用回调函数提高可读性。 - 处理不可信输入、HTML、SQL 或用户提供的正则:先明确安全上下文,不要假设
replace本身会过滤风险。
日常使用中,最容易出错的地方通常是“只替换了第一个”“忘了全局标志”“把字符串当正则”以及“把替换当作安全过滤”。先确定目标是字面文本还是规则匹配,再选参数形式;最后检查返回值是否被接收、正则标志是否正确,以及替换后的字符串将用于什么上下文,基本就能避免大多数问题。




