String.prototype.matchAll

发布日期 · 标签 ECMAScript ES2020

在字符串上重复应用同一个正则表达式以获取所有匹配项是很常见的。在某种程度上,今天已经可以使用 String#match 方法来实现这一点。

在这个例子中,我们找到所有只包含十六进制数字的单词,然后记录每个匹配项

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Output:
//
// 'DEADBEEF'
// 'CAFE'

但是,这只会给你匹配的子字符串。通常,你不仅想要子字符串,还想要其他信息,比如每个子字符串的索引,或者每个匹配项中的捕获组。

通过编写自己的循环并自己跟踪匹配对象,已经可以实现这一点,但这有点烦人,而且不太方便

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

新的 String#matchAll API 使这比以往任何时候都更容易:你现在可以编写一个简单的 for-of 循环来获取所有匹配对象。

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

String#matchAll 对于带有捕获组的正则表达式特别有用。它提供了每个匹配项的完整信息,包括捕获组。

const string = 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} at ${match.index} with '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}

// Output:
//
// tc39/ecma262 at 23 with 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev'
// → owner: tc39
// → repo: ecma262
// v8/v8.dev at 36 with 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev'
// → owner: v8
// → repo: v8.dev

总体思路是,你只需要编写一个简单的 for-of 循环,String#matchAll 会为你处理剩下的事情。

注意:顾名思义,String#matchAll 旨在迭代所有匹配对象。因此,它应该与全局正则表达式一起使用,即那些设置了 g 标志的正则表达式,因为任何非全局正则表达式只会产生一个匹配项(最多)。使用非全局 RegExp 调用 matchAll 会导致 TypeError 异常。

String.prototype.matchAll 支持 #