String.prototype.matchAll 的正则全局匹配

在 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
]
*/

主要特点

  1. 完整匹配信息:每个匹配项都是一个数组,包含完整匹配和所有捕获组
  2. 保留索引信息:包含 index 属性显示匹配位置
  3. 输入字符串引用:包含 input 属性引用原始字符串
  4. 迭代器接口:可以用于 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]}`);
}

注意事项

  1. 必须使用全局正则表达式matchAll() 要求正则表达式必须包含 g 标志,否则会抛出 TypeError
  2. 一次性迭代器:返回的迭代器只能迭代一次,如需多次使用,应转换为数组
  3. 性能考虑:对于非常大的字符串,转换为数组可能消耗较多内存

与传统方法的比较

特性 match() + g exec() 循环 matchAll()
捕获组信息
匹配位置
简洁性
迭代器接口

浏览器兼容性

matchAll() 在现代浏览器中得到广泛支持,包括:

  • Chrome 73+
  • Firefox 67+
  • Safari 13+
  • Edge 79+
  • Node.js 12+

对于旧环境,可以使用 polyfill 或 Babel 转译。

总结

String.prototype.matchAll() 是 ES2020 中一个实用的新增功能,它解决了传统全局匹配方法的信息缺失问题,提供了更完整、更灵活的匹配结果处理方式。对于需要处理复杂正则匹配和捕获组的场景,matchAll() 无疑是更优的选择。