非捕获组:(?:...)

非捕获组对子模式进行分组,允许你将 quantifier 应用于整个组或在其中使用 disjunctions。它的作用类似于 JavaScript 表达式中的 分组运算符,与 捕获组 不同的是,它不会记住匹配的文本,从而在模式还包含有用的捕获组时提供更好的性能并避免混淆。

¥A non-capturing group groups a subpattern, allowing you to apply a quantifier to the entire group or use disjunctions within it. It acts like the grouping operator in JavaScript expressions, and unlike capturing groups, it does not memorize the matched text, allowing for better performance and avoiding confusion when the pattern also contains useful capturing groups.

语法

¥Syntax

regex
(?:pattern)

参数

¥Parameters

pattern

由你可以在正则表达式文字中使用的任何内容组成的模式,包括 disjunction

示例

¥Examples

对子模式进行分组并应用量词

¥Grouping a subpattern and applying a quantifier

在下面的示例中,我们测试文件路径是否以 styles.cssstyles.[a hex hash].css 结尾。因为整个 \.[\da-f]+ 部分是可选的,为了对其应用 ? 量词,我们需要将其分组为一个新的原子。使用非捕获组不会创建我们不需要的额外匹配信息,从而提高性能。

¥In the following example, we test if a file path ends with styles.css or styles.[a hex hash].css. Because the entire \.[\da-f]+ part is optional, in order to apply the ? quantifier to it, we need to group it into a new atom. Using a non-capturing group improves performance by not creating the extra match information that we don't need.

js
function isStylesheet(path) {
  return /styles(?:\.[\da-f]+)?\.css$/.test(path);
}

isStylesheet("styles.css"); // true
isStylesheet("styles.1234.css"); // true
isStylesheet("styles.cafe.css"); // true
isStylesheet("styles.1234.min.css"); // false

对析取进行分组

¥Grouping a disjunction

析取在正则表达式中具有最低优先级。如果你想将析取用作更大模式的一部分,则必须将其分组。建议你使用非捕获组,除非你依赖析取的匹配文本。以下示例匹配文件扩展名,使用与 输入边界断言 文章相同的代码:

¥A disjunction has the lowest precedence in a regular expression. If you want to use a disjunction as a part of a bigger pattern, you must group it. You are advised to use a non-capturing group unless you rely on the matched text of the disjunction. The following example matches file extensions, using the same code as the input boundary assertion article:

js
function isImage(filename) {
  return /\.(?:png|jpe?g|webp|avif|gif)$/i.test(filename);
}

isImage("image.png"); // true
isImage("image.jpg"); // true
isImage("image.pdf"); // false

避免重构危险

¥Avoiding refactoring hazards

捕获组可以通过它们在模式中的位置来访问。如果添加或删除捕获组,则还必须更新其他捕获组的位置(如果你通过匹配结果或 backreferences 访问它们)。这可能是错误的来源,特别是如果大多数组纯粹用于语法目的(应用量词或组析取)。使用非捕获组可以避免此问题,并且可以轻松跟踪实际捕获组的索引。

¥Capturing groups are accessed by their position in the pattern. If you add or remove a capturing group, you must also update the positions of the other capturing groups, if you are accessing them through match results or backreferences. This can be a source of bugs, especially if most groups are purely for syntactic purposes (to apply quantifiers or to group disjunctions). Using non-capturing groups avoids this problem, and allows the indices of actual capturing groups to be easily tracked.

例如,假设我们有一个与字符串中的 title='xxx' 模式匹配的函数(示例取自 捕获组)。为了确保引号匹配,我们使用反向引用来引用第一个引号。

¥For example, suppose we have a function that matches the title='xxx' pattern in a string (example taken from capturing group). To ensure the quotes match, we use a backreference to refer to the first quote.

js
function parseTitle(metastring) {
  return metastring.match(/title=(["'])(.*?)\1/)[2];
}

parseTitle('title="foo"'); // 'foo'

如果我们稍后决定添加 name='xxx' 作为 title= 的别名,我们将需要将析取分组到另一个组中:

¥If we later decided to add name='xxx' as an alias for title=, we will need to group the disjunction in another group:

js
function parseTitle(metastring) {
  // Oops — the backreference and index access are now off by one!
  return metastring.match(/(title|name)=(["'])(.*?)\1/)[2];
}

parseTitle('name="foo"'); // Cannot read properties of null (reading '2')
// Because \1 now refers to the "name" string, which isn't found at the end.

与其找到我们引用捕获组索引的所有位置并一一更新它们,不如避免使用捕获组:

¥Instead of locating all places where we are referring to the capturing groups' indices and updating them one-by-one, it's better to avoid using a capturing group:

js
function parseTitle(metastring) {
  // Do not capture the title|name disjunction
  // because we don't use its value
  return metastring.match(/(?:title|name)=(["'])(.*?)\1/)[2];
}

parseTitle('name="foo"'); // 'foo'

命名捕获组 是避免重构危险的另一种方法。它允许通过自定义名称访问捕获组,添加或删除其他捕获组时不受影响。

¥Named capturing groups are another way to avoid refactoring hazards. It allows capturing groups to accessed by a custom name, which is unaffected when other capturing groups are added or removed.

规范

Specification
ECMAScript Language Specification
# prod-Atom

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看