测试正则表达式的相等性

17

我惊讶地发现

/a/ === /a/

在JavaScript中,正则表达式的比较值为false。通过阅读规范文档

 

程序中的两个正则表达式字面量将计算为永远不会相互比较为===,即使两个字面量的内容相同。

由于无法使用===来测试相等性,因此如何在JavaScript中测试正则表达式的相等性?


6
你在谈论 JavaScript,这种语言中 [] == [] 的计算结果是 False - Tyler Crompton
3
@SivaCharan,那有什么用处吗? - Matt Ball
3
@TylerCrompton:不要忘记[] == [].length这个问题可能会回答原始问题,或者至少能引导 OP 往正确的方向。 - DCoder
1
这里没有什么特别的事情发生 - 这是预期和逻辑行为。 RegExp 不是一个特殊的对象,就像字符串、对象和数组一样 - 你也不会期望 new MyClass(x) === new MyClass(x) 是 true 的。 - Eric
显示剩余3条评论
5个回答

22

这里有一个案例,甚至涉及到标志的排序。

function regexEqual(x, y) {
    return (x instanceof RegExp) && (y instanceof RegExp) && 
           (x.source === y.source) && (x.global === y.global) && 
           (x.ignoreCase === y.ignoreCase) && (x.multiline === y.multiline);
}

测试:

regexEqual(/a/, /a/) // true
regexEqual(/a/gi, /a/ig) // also true.
regeXEqual(/a/, /b/) // false

这不就相当于调用.toString()吗? - Eric
2
@Eric:不是的,.toString() 返回你输入的确切内容。它使用预定义的标志顺序来确保 /a/gi === /a/ig。 - Arka
1
非常好。我不知道 .source 和其他的东西。+1 - Matt Ball
@JohnathonArka:啊,我没考虑过标志的顺序。 - Eric
你还没有检查所有的标志,只有 gim。例如,使用你的函数,regexEqual(/a/s, /a/) 将返回 true。你还需要检查 dotAll (s)、unicode (u) 和 sticky (y) 属性。你可以在这里找到完整的标志列表。 - Donald Duck

8

这里有一个函数,可以完全测试所有相关的正则表达式属性,并确保它是正确类型的对象:

function regexSame(r1, r2) {
    if (r1 instanceof RegExp && r2 instanceof RegExp) {
        var props = ["global", "multiline", "ignoreCase", "source", "dotAll", "sticky", "unicode"];
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            if (r1[prop] !== r2[prop]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

而且,由于有时会为正则表达式对象添加标志以增加新功能(自2012年起已发生过这种情况——尽管上述代码已在2019年进行了更新),因此这里提供了一个更具未来性的版本,它比较任何存在的标志而不是寻找特定的标志集。它在比较之前对标志进行排序,以允许正则表达式的规范方式存在微小差异,这不会实际改变功能。
function regexSame(r1, r2) {
    return r1 instanceof RegExp && 
           r2 instanceof RegExp &&
           r1.source === r2.source &&
           r1.flags.split("").sort().join("") === r2.flags.split("").sort().join("");
}

你还没有检查所有的标志,只有 gim。例如,使用你的函数,regexSame(/a/s, /a/) 将返回 true。你还需要检查 dotAll (s)、unicode (u) 和 sticky (y) 属性。你可以在这里找到完整的标志列表。 - Donald Duck
@DonaldDuck - 好的,我已经添加了这些属性。我不确定这些属性在2012年回答此问题时是否有记录(或者甚至是否支持)。无论如何,现在已经更新了。 - jfriend00
1
@DonaldDuck - 我添加了一个更具未来性的版本,当将来添加新标志时,它仍将继续工作。 - jfriend00
截至2019年12月6日,Edge浏览器不支持正则表达式中的flags属性。Can I use RegExp Flags在Edge浏览器支持该属性之前,第一个答案可能是更好的选择。 - Dan Hooper
1
@DanHooper - 希望很快会实现,因为Edge正在切换到Chromium引擎。 我想这意味着Chrome JS引擎也是如此。 - jfriend00

2
你可以使用typeof检查类型,然后toString()正则表达式并将其进行比较。但它无法涵盖具有等效标志的情况,例如/a/gi/a/ig
function regexEquals(a, b)
{
    if (typeof a !== 'object' || typeof b !== 'object') return false;

    return a.toString() === b.toString();
}

很遗憾,typeof 没有更具体的类型,因此如果您真的想确保它们是正则表达式(或类似于正则表达式),您可以采取以下措施:

RegExp.prototype.regexEquals = function (other)
{
    return (typeof other.regexEquals === 'function')
        && (this.toString() === other.toString());
}

然后:

/a/.regexEquals(/a/); // true
/a/.regexEquals(/b/); // false

你也可以使用 x instanceof RegExp 来检查 x 是否为正则表达式。 - Donald Duck

2

使用toString()进行比较,并检查它们的type

var a = /a/,
    b = /a/;

a.toString() === b.toString() && typeof(a) === typeof(b)  //true

var c = /a/,
    d = /b/;

c.toString() === d.toString() && typeof(c) === typeof(d)  //false

0

上面的答案没有考虑大小写敏感性。因此,在jfriend00的答案的基础上,函数应该是

function regexEqual(a, b) {
    if (!(a instanceof RegExp) || !(b instanceof RegExp)) {
        return false;
    }
    let sourceA = a.source;
    let sourceB = b.source;
    const flagsA = a.flags.split('').sort().join(',');
    const flagsB = b.flags.split('').sort().join(',');
    if (flagsA.includes('i') && flagsB.includes('i')) {
        sourceA = sourceA.toLowerCase();
        sourceB = sourceB.toLowerCase();
    }
    return sourceA === sourceB && flagsA === flagsB;
}

我相信你指出了一个真正的问题,但解决方案是有问题的 - 考虑使用正则表达式,如 /\d/i(一个数字)和 /\D/i(一个非数字)。虽然在这些简单的示例中 /i 没有意义,但很明显,即使输入不区分大小写,正则表达式的源仍然可能是区分大小写的。 - Jacob Raihle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接