16-反向引用
17.14 反向引用
分组使用了一个叫作反向引用的技术。在以往的经验里,这是正则表达式中最不常用的特性之一,但有一种情况使用它很方便。在开始学习真正实用的例子之前,先来看一个非常简单的例子。
假设想匹配符合XYYX格式的乐队名字(作者敢打赌读者肯定可以想出一个符合该格式且真实的乐队名字)。所以当希望匹配PJJP,GOOG和ANNA这些乐队名字时,反向引用就登场了。正则表达式中的每个组(包括子组)都被分配了一个数字,从左到右依次是1、2、3…,可以通过在反斜杠后加一个数字的方式来引用特定的组。换句话说,\1表示“#1组匹配上的所有内容。”有点迷惑了吧?下面来看一个例子:
const promo = "Opening for XAAX is the dynamic GOOG! At the box office now!";
const bands = promo.match(/(?:[A-Z])(?:[A-Z])\2\1/g);
从左往右看,可以找到两个组,然后紧跟着的是 \2\1 。所以,如果第一个组匹配了X,第二个组匹配了A,那么 \2 和 \1 一定分别匹配了 A 和 B 。
如果你觉得这只是看起来很酷却没多大作用,其实作者也有同感。因为在以前的经验中,唯一一次需要使用反向引用(除了解决难题)的场景是引号匹配。
在HTML中,属性值即可以使用单引号也可以使用双引号,这么一来,就可以简单地这样做:
// 这里使用重音符是因为我们将单引号和双引号都用过了
const html = '<img alt='A "simple" example.'>' +
'<img alt="Don't abuse it!"> ';
const matches = html.match(/<img alt=(?:['"]).*?\1/g);
注意,这个例子中做了一些简化工作,如果alt属性不在前面,这个表达式就不会生效,如果有多余空格也不生效。我们将在后面重新回顾这个例子的时候看到如何解决这些问题。
与之前一样,第一个组匹配了单引号或双引号,然后是0个或多个字符(注意匹配问号时用了懒惰匹配),接下来是\1—第一个组匹配上的内容,不管是单引号还是双引号。
下面需要花点时间加强对懒惰匹配和贪婪匹配的理解。试试删除*后面的问号,使其变成贪婪匹配,然后再运行这个表达式,会看到什么?大家知道这是为什么吗?如果想精通正则表达式,这是一个必须掌握的重要概念。所以如果还有不清楚的地方,作者希望大家花点时间再回顾一下懒惰匹配和贪婪匹配那部分的内容。