首页 >社区问答列表 >javascript - 两个等效语义的正则,为何输出的结果却不一致呢

javascript - 两个等效语义的正则,为何输出的结果却不一致呢

代码如下:

'z/yy'.replace(/\/(y|z)|y|z/g, function(name, index) {
    console.log(index, name.length);
    return name == 'y' || name == 'z' ? 'a' : name;
});

正确输出:a/ya

'z/yy'.replace(/\/(y|z)|\1/g, function(name, index) {
    console.log(index, name.length);
    return name == 'y' || name == 'z' ? 'a' : name;
});

错误输出:z/yy

请问为什么呢?

  • 黄舟
  • 黄舟    2017-04-10 14:43:292楼

    @皓矾 的console解释的比较清楚了,唯一不同意的地方是|\1不是匹配所有字符串,而是0长空字符。console里可以看出来。
    这里/\/(y|z)|\1/是分为两个正则分别匹配的:
    1. \/(y|z) 捕获组1是括号中y|z匹配的内容。比如/(y|z)\1/ 匹配yy,zz,但是不匹配yz
    2. \1 捕获组1未定义
    所以2中的\1不会如你所想的匹配y或z
    比较符合你意图的写法可能是
    /\/?(y|z)/

    +0添加回复

  • 回复
  • 小皮
  • 小皮    2017-04-10 14:43:291楼

    这两个正则是不等价的。

    第一个/\/(y|z)|y|z/g,分解开来是\/(y|z)yz。所以,对于字符串z/yy,第一个字符z匹配,被替换为a,/y符合但不等于yz,所以原样输出为/y,最后一个z同理替换为a

    第二个/\/(y|z)|\1/g,比较复杂,展开来说。

    在此之前,先根据MDN的文档重写下函数,因为题主的参数不太正确:

    "z/yy".replace(/\/(y|z)|\1/g, function(matcher, p1, offset, str) {
        console.log(matcher, p1, offset, str);
        return matcher == 'y' || matcher == 'z' ? 'a' : matcher;
    });
    

    其中,matcher是匹配的子字符串,p1是捕捉组匹配的子字符串,既(y|z)捕捉到的,offset是偏移值,str是原字符串。

    执行结果如下:

    //  console.log
    line1 '' undefined 0
    line2 /y y         1
    line3 '' undefined 3
    line4 '' undefined 4
    

    一步步来,

    1. 第一行,偏移值是0,当前监测字符串是z/yymatcher为空,未匹配。
    2. 第二行,偏移值是1,当前监测字符串是/yymatcher/y,捕捉到y。但题主的返回值判断的是matacheryz的比较,所以原样返回/y
    3. 第三行,偏移值是3,当前监测字符串是ymatcher为空,未匹配。
    4. 第三行,偏移值是4,当前监测字符串是``,matcher为空,未匹配。

    关键在第三行,对于字符串'y',正则仍然是/\/(y|z)|\1/g,而不是题主所想的/\/(y|z)|(y|z)/g,所以不匹配。

    顺带研究了下这种|\1写法的正则,这种写法貌似会匹配任何字符串,甚至空字符串。比如:

    /(x)|\1/g.test("fdaf") // return true
    

    甚至:

    /(x)|\1/g.test("") return true
    

    所以,最好还是别这样写……

    +0添加回复

  • 回复