String.prototype.matchAll()

matchAll() 方法返回一个迭代器,该迭代器包含了检索字符串与正则表达式进行匹配的所有结果(包括捕获组)。

尝试一下

语法

js

matchAll(regexp)

参数

regexp

一个正则表达式对象,或者是任何具有 Symbol.matchAll 方法的对象。

如果 regexp 不是一个 RegExp 对象,并且没有 Symbol.matchAll 方法,它将通过 new RegExp(regexp, 'g') 被隐式转换为一个 RegExp 对象。

如果 regexp 是一个正则表达式,那么它必须设置了全局(g)标志,否则会抛出 TypeError 异常。

返回值

一个匹配结果的可迭代迭代器对象 (en-US)(它不可重新开始)。每个匹配结果都是一个数组,其形状与 RegExp.prototype.exec() 的返回值相同。

异常

TypeError

如果 regexp 是一个正则表达式,且没有设置全局(g)标志(其 flags 属性不包含 "g"),则会抛出该异常。

描述

String.prototype.matchAll 方法本身的实现非常简单,它只是调用了参数的 Symbol.matchAll 方法,并将字符串作为第一个参数传递了进去(除了额外的输入验证,即正则表达式必须是全局的)。实际的实现来自 RegExp.prototype[@@matchAll]()

示例

Regexp.prototype.exec() 和 matchAll()

如果没有 matchAll() 方法,仍然可以使用带有 g 标志的正则表达式调用 regexp.exec() 来在循环中获取所有匹配结果:

js

const regexp = /foo[a-z]*/g;
const str = "table football, foosball";
let match;

while ((match = regexp.exec(str)) !== null) {
  console.log(
    `找到 ${match[0]} 起始位置=${match.index} 结束位置=${regexp.lastIndex}`,
  );
}
// 找到 football 起始位置=6 结束位置=14。
// 找到 foosball 起始位置=16 结束位置=24。

如果有 matchAll() 方法,则可以避免使用 while 循环和带有 g 标志的 exec。相反,你可以获得一个迭代器,以便使用更方便的 for...of 循环、数组展开语法Array.from() 构造函数:

js

const regexp = /foo[a-z]*/g;
const str = "table football, foosball";
const matches = str.matchAll(regexp);

for (const match of matches) {
  console.log(
    `找到 ${match[0]} 起始位置=${match.index} 结束位置=${
      match.index + match[0].length
    }.`,
  );
}
// 找到 football 起始位置=6 结束位置=14.
// 找到 foosball 起始位置=16 结束位置=24.

// 匹配迭代器在 for...of 迭代后用尽
// 再次调用 matchAll 以创建新的迭代器
Array.from(str.matchAll(regexp), (m) => m[0]);
// [ "football", "foosball" ]

如果没有 g 标志,matchAll 会抛出异常。

js

const regexp = /[a-c]/;
const str = "abc";
str.matchAll(regexp);
// TypeError

matchAll 内部做了一个 regexp 的复制,所以不像 regexp.exec()lastIndex 在字符串扫描后不会改变。

js

const regexp = /[a-c]/g;
regexp.lastIndex = 1;
const str = "abc";
Array.from(str.matchAll(regexp), (m) => `${regexp.lastIndex} ${m[0]}`);
// [ "1 b", "1 c" ]

然而,这也意味着,与在循环中使用 regexp.exec() 不同,你不能更改 lastIndex 来使正则表达式前进或倒退。

比 String.prototype.match() 更好的捕获组获取方式

matchAll() 方法的另一个重要优点是改进了对于捕获组的获取方式。

当使用全局 g 标志调用 match() 方法时,捕获组会被忽略:

js

const regexp = /t(e)(st(\d?))/g;
const str = "test1test2";

str.match(regexp); // ['test1', 'test2']

使用 matchAll 方法,可以轻松获取捕获组:

js

const array = [...str.matchAll(regexp)];

array[0];
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test2', length: 4]
array[1];
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', length: 4]

使用实现了 @@matchAll 的非正则对象调用 matchAll()

如果一个对象有一个 Symbol.matchAll 方法,它可以被用作自定义匹配器。Symbol.matchAll 的返回值将成为 matchAll() 的返回值。

js

const str = "Hmm, this is interesting.";

str.matchAll({
  [Symbol.matchAll](str) {
    return [["Yes, it's interesting."]];
  },
}); // returns [["Yes, it's interesting."]]

规范

Specification
ECMAScript Language Specification
# sec-string.prototype.matchall

浏览器兼容性

BCD tables only load in the browser

参见