For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/year/2020/proposal-string-matchall.md.
  • 简体中文
  • String.prototype.matchAll S4

    提案概览
    提案速览

    该提案添加了 String.prototype.matchAll,它返回一个迭代器,用于遍历全局或粘性正则表达式的所有匹配项,包括捕获组。它解决了 String.prototype.match 在使用全局标志时不提供捕获组的问题,并且避免了手动使用 exec 或 replace 的副作用。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    String.prototype.matchAll

    String.prototype.matchAll 的提案和规范。

    Polyfill/Shim

    参见 npm 上的 string.prototype.matchallgithub 上的

    规范

    你可以查看 markdown 格式 的规范,或渲染为 HTML 的版本。

    理由

    如果我有一个字符串,并且有一个粘性(sticky)或全局(global)的正则表达式,该表达式具有多个捕获组,我通常希望遍历所有匹配项。 目前,我的选择如下:

    var regex = /t(e)(st(\d?))/g;
    var string = 'test1test2';
    
    string.match(regex); // 返回 ['test1', 'test2'] - 我如何获取捕获组?
    
    var matches = [];
    var lastIndexes = {};
    var match;
    lastIndexes[regex.lastIndex] = true;
    while (match = regex.exec(string)) {
    	lastIndexes[regex.lastIndex] = true;
    	matches.push(match);
    	// 示例:['test1', 'e', 'st1', '1'],带有 `index` 和 `input` 属性
    }
    matches; /* 这正是我想要的,但使用了循环,
    		* 并且修改了正则表达式的 `lastIndex` 属性 */
    lastIndexes; /* 理想情况下应返回 { 0: true },但实际
    		* 对 lastIndex 的每次修改都会有一个值 */
    
    var matches = [];
    string.replace(regex, function () {
    	var match = Array.prototype.slice.call(arguments, 0, -2);
    	match.input = arguments[arguments.length - 1];
    	match.index = arguments[arguments.length - 2];
    	matches.push(match);
    	// 示例:['test1', 'e', 'st1', '1'],带有 `index` 和 `input` 属性
    });
    matches; /* 这正是我想要的,但滥用了 `replace`,
    	  * 修改了正则表达式的 `lastIndex` 属性,
    	  * 并且需要手动构建 `match` */

    第一个示例不提供捕获组,因此不是一个选项。后两个示例都明显修改了 lastIndex - 对于内置的 RegExp 来说,这不是一个巨大的问题(超越了意识形态),但在 ES6/ES2015 中,对于可子类化的 RegExp,这是一种获取所有匹配所需信息的混乱方式。

    因此,String#matchAll 将通过提供对所有捕获组的访问,并且不明显修改所涉及的正则表达式对象来解决这个用例。

    迭代器与数组

    许多用例可能想要一个匹配项的数组 - 但显然并非所有用例都需要。特别多的捕获组或大字符串,如果总是将所有匹配项收集到数组中,可能会产生性能影响。通过返回一个迭代器,如果调用者愿意,可以轻松地使用展开运算符或 Array.from 将其收集到数组中,但这不是必须的。

    先前的讨论

    命名

    选择 matchAll 这个名称是为了与 match 对应,并暗示将返回 所有 匹配项,而不仅仅是单个匹配。这包括这样的含义:提供的正则表达式将使用全局标志来查找字符串中的所有匹配。有人建议使用另一个名称 matches - 这遵循了 keys/values/entries 的先例,即复数名词表示返回迭代器。但是,includes 返回布尔值。当单词不明确是名词还是动词时,"复数名词"似乎不是一个明显的约定。

    委员会反馈更新:ruby 使用 scan 这个词,但委员会不倾向于向 JavaScript 引入一个新词。有人建议使用 matchEach,但一些人对它与 forEach 的名称相似性感到不适,因为 API 相当不同。matchAll 似乎是大家最满意的名称。

    在 2017 年 9 月的 TC39 会议上,有人提出了一个问题:"all" 是指 "所有重叠匹配" 还是 "所有非重叠匹配" - 其中 "重叠" 指 "从字符串中每个字符开始的匹配","非重叠" 指 "从字符串开头开始的所有匹配"。我们简要考虑了重命名该方法或添加一种实现两种语义的方法,但该异议被撤回。