String.prototype.replace()

replace()String 值的一个方法,它返回一个新字符串,其中一个、一些或所有匹配的 patternreplacement 替换。pattern 可以是字符串或 RegExpreplacement 可以是字符串或为每个匹配调用的函数。如果 pattern 是字符串,则只替换第一个出现的匹配项。原始字符串保持不变。

试一试

语法

js
replace(pattern, replacement)

参数

pattern

可以是字符串或具有 Symbol.replace 方法的对象——典型的例子是 正则表达式。任何没有 Symbol.replace 方法的值都将被强制转换为字符串。

replacement

可以是字符串或函数。

  • 如果它是字符串,它将替换与 pattern 匹配的子字符串。支持许多特殊的替换模式;请参阅下面的 指定字符串作为替换 部分。
  • 如果它是一个函数,它将为每个匹配调用,并且它的返回值用作替换文本。提供给此函数的参数在下面的 指定函数作为替换 部分中进行了描述。

返回值

一个新字符串,其中一个、一些或所有匹配的模式被指定的替换替换。

描述

此方法不会改变它被调用的字符串值。它返回一个新字符串。

字符串模式将只被替换一次。要执行全局搜索和替换,请使用带有 g 标志的正则表达式,或改用 replaceAll()

如果 pattern 是一个具有 Symbol.replace 方法的对象(包括 RegExp 对象),则该方法将以目标字符串和 replacement 作为参数调用。它的返回值成为 replace() 的返回值。在这种情况下,replace() 的行为完全由 [Symbol.replace]() 方法编码——例如,下面描述中任何提到“捕获组”的内容实际上都是由 RegExp.prototype[Symbol.replace]() 提供的功能。

如果 pattern 是一个空字符串,则替换将附加到字符串的开头。

js
"xxx".replace("", "_"); // "_xxx"

带有 g 标志的正则表达式是 replace() 替换多次的唯一情况。有关正则表达式属性(尤其是 粘性 标志)如何与 replace() 交互的更多信息,请参阅 RegExp.prototype[Symbol.replace]()

指定字符串作为替换

替换字符串可以包含以下特殊的替换模式

模式 插入
$$ 插入一个 "$"
$& 插入匹配的子字符串。
$` 插入匹配的子字符串之前的字符串部分。
$' 插入匹配的子字符串之后的字符串部分。
$n 插入第 n 个(从 1 开始索引)捕获组,其中 n 是小于 100 的正整数。
$<Name> 插入命名捕获组,其中 Name 是组名。

$n$<Name> 仅在 pattern 参数为 RegExp 对象时可用。如果 pattern 是字符串,或者正则表达式中不存在相应的捕获组,则该模式将被替换为字面量。如果该组存在但未匹配(因为它属于一个并列条件的一部分),则它将被替换为空字符串。

js
"foo".replace(/(f)/, "$2");
// "$2oo"; the regex doesn't have the second group

"foo".replace("f", "$1");
// "$1oo"; the pattern is a string, so it doesn't have any groups

"foo".replace(/(f)|(g)/, "$2");
// "oo"; the second group exists but isn't matched

将函数指定为替换值

您可以将函数指定为第二个参数。在这种情况下,在执行匹配后将调用该函数。该函数的结果(返回值)将用作替换字符串。

注意:上述特殊替换模式适用于替换函数返回的字符串。

该函数具有以下签名

js
function replacer(match, p1, p2, /* …, */ pN, offset, string, groups) {
  return replacement;
}

函数的参数如下

match

匹配的子字符串。(对应于上面的 $&。)

p1p2、…、pN

捕获组找到的第 n 个字符串(包括命名捕获组),前提是 replace() 的第一个参数是 RegExp 对象。(对应于上面的 $1$2 等。)例如,如果 pattern/(\a+)(\b+)/,则 p1\a+ 的匹配项,p2\b+ 的匹配项。如果该组是并列条件的一部分(例如 "abc".replace(/(a)|(b)/, replacer)),则未匹配的备选方案将为 undefined

offset

匹配的子字符串在正在检查的整个字符串中的偏移量。例如,如果整个字符串是 'abcd',而匹配的子字符串是 'bc',则此参数将为 1

string

正在检查的整个字符串。

groups

一个对象,其键是使用的组名,其值是匹配的部分(如果未匹配则为 undefined)。仅当 pattern 包含至少一个命名捕获组时才存在。

参数的确切数量取决于第一个参数是否为 RegExp 对象——以及如果是,它有多少个捕获组。

以下示例将 newString 设置为 'abc - 12345 - #$*%'

js
function replacer(match, p1, p2, p3, offset, string) {
  // p1 is non-digits, p2 digits, and p3 non-alphanumerics
  return [p1, p2, p3].join(" - ");
}
const newString = "abc12345#$*%".replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString); // abc - 12345 - #$*%

如果第一个参数中的正则表达式是全局的,则对于要替换的每个完整匹配,该函数将被多次调用。

示例

在 replace() 中定义正则表达式

在以下示例中,正则表达式是在 replace() 中定义的,并且包含不区分大小写标志。

js
const str = "Twas the night before Xmas...";
const newstr = str.replace(/xmas/i, "Christmas");
console.log(newstr); // Twas the night before Christmas...

这将输出 'Twas the night before Christmas...'

注意:有关正则表达式的更多说明,请参阅 正则表达式指南

在 replace() 中使用全局和不区分大小写标志

全局替换只能使用正则表达式完成。在以下示例中,正则表达式包含 全局和不区分大小写标志,这允许 replace() 将字符串中每个出现的 'apples' 替换为 'oranges'

js
const re = /apples/gi;
const str = "Apples are round, and apples are juicy.";
const newstr = str.replace(re, "oranges");
console.log(newstr); // oranges are round, and oranges are juicy.

这将输出 'oranges are round, and oranges are juicy'

切换字符串中的单词

以下脚本切换字符串中的单词。对于替换文本,脚本使用 捕获组$1$2 替换模式。

js
const re = /(\w+)\s(\w+)/;
const str = "Maria Cruz";
const newstr = str.replace(re, "$2, $1");
console.log(newstr); // Cruz, Maria

这将输出 'Cruz, Maria'

使用修改匹配字符的内联函数

在此示例中,字符串中所有出现的大写字母都转换为小写,并且在匹配位置之前插入一个连字符。这里重要的是,在将匹配项作为替换项返回之前,需要对其进行其他操作。

替换函数接受匹配的片段作为其参数,并使用它来转换大小写并在返回之前连接连字符。

js
function styleHyphenFormat(propertyName) {
  function upperToHyphenLower(match, offset, string) {
    return (offset > 0 ? "-" : "") + match.toLowerCase();
  }
  return propertyName.replace(/[A-Z]/g, upperToHyphenLower);
}

给定 styleHyphenFormat('borderTop'),这将返回 'border-top'

因为我们希望在进行最终替换之前进一步转换匹配的结果,所以必须使用函数。这会强制在 toLowerCase() 方法之前评估匹配项。如果我们尝试使用没有函数的匹配项来执行此操作,则 toLowerCase() 将不起作用。

js
// Won't work
const newString = propertyName.replace(/[A-Z]/g, "-" + "$&".toLowerCase());

这是因为 '$&'.toLowerCase() 将首先被评估为字符串字面量(导致相同的 '$&'),然后才使用这些字符作为模式。

将华氏度替换为其摄氏度等值

以下示例将华氏度替换为其等值的摄氏度。华氏度应以 "F" 结尾的数字。该函数返回以 "C" 结尾的摄氏度数。例如,如果输入数字是 "212F",则该函数返回 "100C"。如果数字是 "0F",则该函数返回 "-17.77777777777778C"

正则表达式 test 检查任何以 F 结尾的数字。华氏度的数量可以通过其第二个参数 p1 访问该函数。该函数根据传递给 f2c() 函数的字符串中的华氏度数设置摄氏度数。f2c() 然后返回摄氏度数。此函数近似于 Perl 的 s///e 标志。

js
function f2c(x) {
  function convert(str, p1, offset, s) {
    return `${((p1 - 32) * 5) / 9}C`;
  }
  const s = String(x);
  const test = /(-?\d+(?:\.\d*)?)F\b/g;
  return s.replace(test, convert);
}

创建通用替换器

假设我们想要创建一个替换器,它将偏移数据附加到每个匹配的字符串。因为替换函数已经接收了 offset 参数,所以在静态知道正则表达式的情况下,这将非常简单。

js
"abcd".replace(/(bc)/, (match, p1, offset) => `${match} (${offset}) `);
// "abc (1) d"

但是,如果我们希望它适用于任何正则表达式模式,则此替换器将难以泛化。替换器是可变的——它接收的参数数量取决于存在的捕获组的数量。我们可以使用 剩余参数,但它也会将 offsetstring 等收集到数组中。根据正则表达式的标识,groups 是否传递也会使其难以通用地知道哪个参数对应于 offset

js
function addOffset(match, ...args) {
  const offset = args.at(-2);
  return `${match} (${offset}) `;
}

console.log("abcd".replace(/(bc)/, addOffset)); // "abc (1) d"
console.log("abcd".replace(/(?<group>bc)/, addOffset)); // "abc (abcd) d"

上面的 addOffset 示例在正则表达式包含命名组时不起作用,因为在这种情况下,args.at(-2) 将是 string 而不是 offset

相反,您需要根据类型提取最后几个参数,因为 groups 是一个对象,而 string 是一个字符串。

js
function addOffset(match, ...args) {
  const hasNamedGroups = typeof args.at(-1) === "object";
  const offset = hasNamedGroups ? args.at(-3) : args.at(-2);
  return `${match} (${offset}) `;
}

console.log("abcd".replace(/(bc)/, addOffset)); // "abc (1) d"
console.log("abcd".replace(/(?<group>bc)/, addOffset)); // "abc (1) d"

规范

规范
ECMAScript 语言规范
# sec-string.prototype.replace

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅