在 ECMAScript 2020 (ES11) 中,JavaScript 引入了一个强大的新字符串方法 String.prototype.matchAll()
,它为处理正则表达式全局匹配提供了更优雅和功能完整的解决方案。
传统全局匹配的问题
在 matchAll()
出现之前,开发者通常使用 String.prototype.match()
方法配合 g
标志进行全局匹配:
javascript
const str = 'test1test2';
const regex = /t(e)(st(\d?))/g;
const result = str.match(regex);
console.log(result); // ["test1", "test2"]
这种方法存在明显局限性:
- 只返回匹配的子字符串,不包含捕获组信息
- 无法获取每个匹配项的详细信息(如索引位置)
matchAll() 的解决方案
matchAll()
方法返回一个迭代器,包含所有匹配项的完整信息:
javascript
const str = 'test1test2';
const regex = /t(e)(st(\d?))/g;
const matches = str.matchAll(regex);
for (const match of matches) {
console.log(match);
}
/*
[
'test1',
'e',
'st1',
'1',
index: 0,
input: 'test1test2',
groups: undefined
]
[
'test2',
'e',
'st2',
'2',
index: 5,
input: 'test1test2',
groups: undefined
]
*/
主要特点
- 完整匹配信息:每个匹配项都是一个数组,包含完整匹配和所有捕获组
- 保留索引信息:包含
index
属性显示匹配位置 - 输入字符串引用:包含
input
属性引用原始字符串 - 迭代器接口:可以用于
for...of
循环或转换为数组
实际应用示例
提取所有匹配项及其位置
javascript
const text = 'Hello world, hello JavaScript, hello ES2020';
const regex = /hello/gi;
const matches = [...text.matchAll(regex)];
matches.forEach(match => {
console.log(`Found "${match[0]}" at position ${match.index}`);
});
处理复杂捕获组
javascript
const html = '<div><span>text1</span></div><div>text2</div>';
const regex = /<(\w+)>([^<]*)<\/\1>/g;
for (const match of html.matchAll(regex)) {
console.log(`Tag: ${match[1]}, Content: ${match[2]}`);
}
注意事项
- 必须使用全局正则表达式:
matchAll()
要求正则表达式必须包含g
标志,否则会抛出 TypeError - 一次性迭代器:返回的迭代器只能迭代一次,如需多次使用,应转换为数组
- 性能考虑:对于非常大的字符串,转换为数组可能消耗较多内存
与传统方法的比较
特性 | match() + g | exec() 循环 | matchAll() |
---|---|---|---|
捕获组信息 | ❌ | ✅ | ✅ |
匹配位置 | ❌ | ✅ | ✅ |
简洁性 | ✅ | ❌ | ✅ |
迭代器接口 | ❌ | ❌ | ✅ |
浏览器兼容性
matchAll()
在现代浏览器中得到广泛支持,包括:
- Chrome 73+
- Firefox 67+
- Safari 13+
- Edge 79+
- Node.js 12+
对于旧环境,可以使用 polyfill 或 Babel 转译。
总结
String.prototype.matchAll()
是 ES2020 中一个实用的新增功能,它解决了传统全局匹配方法的信息缺失问题,提供了更完整、更灵活的匹配结果处理方式。对于需要处理复杂正则匹配和捕获组的场景,matchAll()
无疑是更优的选择。