非捕获组:(?:...)
**非捕获组**对子模式进行分组,允许您将量词应用于整个组或在其中使用析取。它类似于 JavaScript 表达式中的分组运算符,并且与捕获组不同,它不会记住匹配的文本,从而提高性能并避免模式也包含有用的捕获组时产生的混淆。
语法
正则表达式
(?:pattern)
参数
示例
对子模式进行分组并应用量词
在以下示例中,我们测试文件路径是否以styles.css
或styles.[a hex hash].css
结尾。因为整个\.[\da-f]+
部分是可选的,所以为了将?
量词应用于它,我们需要将其分组为一个新的原子。使用非捕获组通过不创建我们不需要的额外匹配信息来提高性能。
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
对析取进行分组
析取在正则表达式中具有最低优先级。如果您想将析取用作更大模式的一部分,则必须对其进行分组。除非您依赖于析取的匹配文本,否则建议您使用非捕获组。以下示例使用与输入边界断言文章相同的代码匹配文件扩展名
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
避免重构风险
捕获组通过其在模式中的位置进行访问。如果您添加或删除捕获组,则还必须更新其他捕获组的位置,如果您是通过匹配结果或反向引用访问它们。这可能是错误的根源,尤其是在大多数组纯粹用于语法目的(应用量词或分组析取)时。使用非捕获组可以避免此问题,并允许轻松跟踪实际捕获组的索引。
例如,假设我们有一个函数匹配字符串中的title='xxx'
模式(示例取自捕获组)。为了确保引号匹配,我们使用反向引用来引用第一个引号。
js
function parseTitle(metastring) {
return metastring.match(/title=(["'])(.*?)\1/)[2];
}
parseTitle('title="foo"'); // 'foo'
如果我们稍后决定添加name='xxx'
作为title=
的别名,我们将需要将析取分组到另一个组中
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.
与其查找所有引用捕获组索引的位置并逐一更新它们,不如避免使用捕获组
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'
命名捕获组是避免重构风险的另一种方法。它允许通过自定义名称访问捕获组,当添加或删除其他捕获组时,该名称不受影响。
规范
规范 |
---|
ECMAScript 语言规范 # prod-Atom |
浏览器兼容性
BCD 表格仅在浏览器中加载