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 旗標的正規表示法,因為任何非全域正規表示法只會產生一個符合條件的項目(最多)。使用非全域正規表示法呼叫 matchAll 會導致 TypeError 例外狀況。

String.prototype.matchAll 支援 #