主持人注意:请抵制编辑代码或删除此通知的冲动。空白模式可能是问题的一部分,因此不应进行不必要的篡改。如果您处于“空白是无关紧要的”阵营,您应该能够接受代码。

在JavaScript中,(a==1&&a==2&&a==3)是否有可能求值为真?

这是一家大型科技公司提出的面试问题。事情发生在两周前,但我仍在努力寻找答案。我知道我们在日常工作中从未编写过这样的代码,但我很好奇。


当前回答

通过在类声明中重写valueOf,可以执行以下操作:

class Thing {
    constructor() {
        this.value = 1;
    }

    valueOf() {
        return this.value++;
    }
}

const a = new Thing();

if(a == 1 && a == 2 && a == 3) {
    console.log(a);
}

发生的情况是在每个比较运算符中调用valueOf。第一次,a等于1,第二次,a将等于2,依此类推,因为每次调用valueOf时,a的值都会递增。

因此,console.log将激发并输出(在我的终端中)Thing:{value:4},表示条件为真。

其他回答

令人惊讶的是,是的。JS中的==松散相等运算符调用被比较对象的valueOf()方法。因此,您可以创建一个类,该类返回一个内部值,然后在每次调用时递增该间隔值。这样地:

A类{构造函数(initalVal){this.val=初始值;}值Of(){返回此.val++;}}const a=新AClass(1);console.log(a==1&&a==2&&a==3)

我知道这个问题还有很多其他答案,但这就是ES6语法的用法。

注意:如果不希望发生这种情况,那么应该使用==运算符来检查strict。这样地:

A类{构造函数(initalVal){this.val=初始值;}值Of(){返回此.val++;}}const a=新AClass(1);console.log(a==1&&a==2&&a==3)

如果没有正则表达式就无法执行任何操作:

变量a={r: /\d/g,valueOf:函数(){返回此.r.exec(123)[0]}}如果(a==1&&a==2&&a==3){console.log(“!”)}

它之所以有效,是因为当Object与基元(如Number)进行比较时调用了自定义valueOf方法。主要技巧是,a.valueOf每次都会返回新值,因为它使用g标志对正则表达式调用exec,这会导致每次找到匹配项时更新该正则表达式的lastIndex。所以第一次this.r.lastIndex==0时,它匹配1并更新lastIndex:this.r.lastIndex==1,所以下次regex将匹配2,依此类推。

面试规则一;永远不要说不可能。

不需要隐藏角色的诡计。

窗口__定义Getter__('a',函数(){if(类型i!=='number'){//在全局命名空间中定义i,以便在运行此函数后不会丢失i=0;}返回++i;});如果(a==1&&a==2&&a==3){console.log(“哦,亲爱的,我们做了什么?”);}

这是@Jeff的答案*的反转版本,其中使用隐藏字符(U+115F、U+1160或U+3164)创建类似于1、2和3的变量。

变量a=1;变量ᅠ1=a;变量ᅠ2=a;变量ᅠ3=a;console.log(a==ᅠ1和a==ᅠ2&&a(&&a)==ᅠ3 );

*这个答案可以通过使用零宽度非接合(U+200C)和零宽度接合(U+200D)来简化。这两个字符都允许在标识符内,但不能在开头:

变量a=1;变量a‌ = 2.变量a‍ = 3.console.log(a==1&a‌ == 2&&a(&&a)‍ == 3);/****变量a=1;var a\u200c=2;var a\u200d=3;console.log(a==1&&a\u200c==2&&a\u200d==3);****/

其他技巧也可以使用相同的思想,例如使用Unicode变体选择器创建看起来完全相同的变量(a︀ = 1.一︁ = 2.一︀ == 1和a︁ == 2; // 真)。

使用符号的ECMAScript 6答案:

const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));

由于==的用法,JavaScript应该强制a接近第二个操作数(本例中为1、2、3)。但是,在JavaScript尝试自己计算强制之前,它会尝试调用Symbol.toPrimitive。如果您提供Symbol.toPrimitive,JavaScript将使用函数返回的值。如果没有,JavaScript将调用valueOf。