RegExp.prototype.exec()

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

exec() 方法用于在指定字符串中执行正则表达式搜索匹配,并返回一个结果数组,或者 null

试一试

const regex = /fo+/g;
const str = "table football, foosball";
let array;

while ((array = regex.exec(str)) !== null) {
  console.log(`Found ${array[0]}. Next starts at ${regex.lastIndex}.`);
  // Expected output: "Found foo. Next starts at 9."
  // Expected output: "Found foo. Next starts at 19."
}

语法

js
exec(str)

参数

str

要匹配的正则表达式的字符串。所有值都会被 强制转换为字符串,因此省略它或传递 undefined 会导致 exec() 搜索字符串 "undefined",这通常不是你想要的。

返回值

如果匹配失败,exec() 方法返回 null,并将正则表达式的 lastIndex 设置为 0

如果匹配成功,exec() 方法返回一个数组,并更新正则表达式对象的 lastIndex 属性。返回的数组将匹配的文本作为第一个元素,然后为匹配文本的每个捕获组提供一个元素。数组还具有以下附加属性:

index

匹配在字符串中的 0 索引。

input

用于匹配的原始字符串。

groups

一个 null-prototype 对象,包含命名捕获组,其键是名称,值是捕获组,如果没有定义命名捕获组,则值为 undefined。有关更多信息,请参阅 捕获组

indices 可选

仅当设置了 d 标志时,才会出现此属性。它是一个数组,其中每个条目代表一个子字符串匹配的边界。此数组中每个元素的索引对应于 exec() 返回的数组中相应子字符串匹配的索引。换句话说,第一个 indices 条目代表整个匹配,第二个 indices 条目代表第一个捕获组,依此类推。每个条目本身是一个包含两个元素的数组,第一个数字代表匹配的开始索引,第二个数字代表其结束索引。

indices 数组还额外有一个 groups 属性,其中包含所有命名捕获组的 null-prototype 对象。键是捕获组的名称,每个值是一个包含两个元素的数组,第一个数字是开始索引,第二个数字是捕获组的结束索引。如果正则表达式不包含任何命名捕获组,则 groupsundefined

描述

当设置了 全局粘性 标志(例如,/foo/g/foo/y)时,JavaScript RegExp 对象是有状态的。它们会存储上一次匹配的 lastIndex。通过内部使用此属性,exec() 可以用于迭代字符串中的多个匹配(带有捕获组),而不是像 String.prototype.match() 那样只获取匹配的字符串。

使用 exec() 时,如果设置了粘性标志,全局标志将不起作用——匹配始终是粘性的。

exec() 是正则表达式的原生方法。许多其他正则表达式方法在内部调用 exec() — 包括字符串方法调用的那些,例如 [Symbol.replace]()。虽然 exec() 本身功能强大(并且效率最高),但它通常不能最清晰地表达意图。

  • 如果你只关心正则表达式是否匹配一个字符串,而不关心实际匹配了什么,请改用 RegExp.prototype.test()
  • 如果你正在查找全局正则表达式的所有匹配项,并且不关心捕获组等信息,请改用 String.prototype.match()。此外,String.prototype.matchAll() 可以通过允许你迭代匹配项来简化匹配字符串的多个部分(带有捕获组)。
  • 如果你执行匹配是为了在字符串中找到其索引位置,请改用 String.prototype.search() 方法。

exec() 对于上面任何方法都难以实现的复杂操作很有用,通常在你需要手动调整 lastIndex 时。(String.prototype.matchAll() 会复制正则表达式,因此在迭代 matchAll 时更改 lastIndex 不会影响迭代。) 其中一个例子请参阅 重置 lastIndex

示例

使用 exec()

考虑以下示例

js
// Match "quick brown" followed by "jumps", ignoring characters in between
// Remember "brown" and "jumps"
// Ignore case
const re = /quick\s(?<color>brown).+?(jumps)/dgi;
const result = re.exec("The Quick Brown Fox Jumps Over The Lazy Dog");

下表显示了运行此脚本后的 result 状态

属性
[0] "Quick Brown Fox Jumps"
[1] "Brown"
[2] "Jumps"
index 4
indices [[4, 25], [10, 15], [20, 25]]
groups: { color: [10, 15 ]}
input "The Quick Brown Fox Jumps Over The Lazy Dog"
groups { color: "Brown" }

此外,re.lastIndex 将被设置为 25,因为这个正则表达式是全局的。

查找连续匹配项

如果你的正则表达式使用了 g 标志,你可以多次使用 exec() 方法在同一个字符串中查找连续的匹配项。这样做时,搜索将从正则表达式的 lastIndex 属性指定的 str 子字符串开始(test() 也会推进 lastIndex 属性)。请注意,在搜索不同字符串时,lastIndex 属性不会被重置,它将从其当前 lastIndex 开始搜索。

例如,假设你有以下脚本

js
const myRe = /ab*/g;
const str = "abbcdefabh";
let myArray;
while ((myArray = myRe.exec(str)) !== null) {
  let msg = `Found ${myArray[0]}. `;
  msg += `Next match starts at ${myRe.lastIndex}`;
  console.log(msg);
}

此脚本显示以下文本

Found abb. Next match starts at 3
Found ab. Next match starts at 9

警告: 有许多陷阱可能导致这种情况变成无限循环!

  • 不要将正则表达式字面量(或 RegExp 构造函数)放在 while 条件内 — 它将在每次迭代时重新创建正则表达式并重置 lastIndex
  • 确保设置了 全局 (g) 标志,否则 lastIndex 将永远不会被推进。
  • 如果正则表达式可能匹配零长度字符(例如,/^/gm),请每次手动增加其 lastIndex 以避免停留在同一位置。

你通常可以用 String.prototype.matchAll() 替换此类代码,使其更不容易出错。

使用 exec() 和 RegExp 字面量

你也可以在不显式创建 RegExp 对象的情况下使用 exec()

js
const matches = /(hello \S+)/.exec("This is a hello world!");
console.log(matches[1]);

这将记录一条包含 'hello world!' 的消息。

规范

规范
ECMAScript® 2026 语言规范
# sec-regexp.prototype.exec

浏览器兼容性

另见