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

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

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


当前回答

没有getters或valueOf的示例:

a=[1,2,3];a.join=a.shift;console.log(a==1&&a==2&&a==3);

这之所以有效,是因为==调用toString,它为数组调用.join。

另一种解决方案,使用Symbol.toPrimitive,它是ES6等价于String/valueOf:

设i=0;让a={[Symbol.toPrimitive]:()=>++i};console.log(a==1&&a==2&&a==3);

其他回答

好了,又一个发电机黑客:

常量值=函数*(){设i=0;而(真)产量++i;}();Object.defineProperty(this,“a”{获取(){return value.next().value;}});如果(a==1&&a==2&&a==3){console.log(“哟!”);}

老实说,不管有没有一种方法可以评估它的真实性(正如其他人所展示的,有多种方法),我要寻找的答案是,作为一个已经进行了数百次面试的人来说,大致如下:

“嗯,也许是的,在一些奇怪的情况下,这些情况对我来说不是很明显……但如果我在真实的代码中遇到了这种情况,我会使用常见的调试技术来弄清楚它是如何以及为什么在做它正在做的事情,然后立即重构代码以避免这种情况……但更重要的是:我绝对不会在一开始就写代码,因为这就是复杂代码的定义,我努力不写复杂代码”。

我想有些面试官会对提出一个显然是非常棘手的问题感到愤怒,但我不介意有意见的开发人员,特别是当他们能够用合理的想法来支持这一点,并将我的问题融入到一个关于自己的有意义的陈述中时。

没有getters或valueOf的示例:

a=[1,2,3];a.join=a.shift;console.log(a==1&&a==2&&a==3);

这之所以有效,是因为==调用toString,它为数组调用.join。

另一种解决方案,使用Symbol.toPrimitive,它是ES6等价于String/valueOf:

设i=0;让a={[Symbol.toPrimitive]:()=>++i};console.log(a==1&&a==2&&a==3);

这是可能的!

变量i=0;具有({获取a(){返回++i;}}) {如果(a==1&&a==2&&a==3)console.log(“wohoo”);}

这在with语句中使用getter,让一个求值为三个不同的值。

…这仍然不意味着这应该在实际代码中使用。。。

更糟糕的是,使用==也可以使用此技巧。

变量i=0;具有({获取a(){返回++i;}}) {如果(a!==a)console.log(“是的,这是打印的。”);}

使用代理:

var a = new Proxy({ i: 0 }, {
    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3);

代理基本上假装是目标对象(第一个参数),但拦截目标对象上的操作(在本例中是“获取属性”操作),以便有机会执行默认对象行为以外的其他操作。在这种情况下,对when==强制其类型调用“get property”操作,以便将其与每个数字进行比较。发生这种情况:

我们创建一个目标对象{i:0},其中i属性是我们的计数器我们为目标对象创建代理,并将其分配给对于每个a==比较,a的类型都被强制为基元值这种类型强制导致在内部调用[Symbol.toPrimitive]()Proxy使用“get handler”拦截获取[Symbol.toPrimitive]函数代理的“获取处理程序”检查正在获取的属性是否为Symbol.toPrimitive,在这种情况下,它会递增,然后从目标对象返回计数器:++target.i。如果正在检索不同的属性,我们只需返回默认属性值target[name]

So:

var a = ...; // a.valueOf == target.i == 0
a == 1 && // a == ++target.i == 1
a == 2 && // a == ++target.i == 2
a == 3    // a == ++target.i == 3

与大多数其他答案一样,这只适用于松散的等式检查(==),因为严格的等式检查不执行代理可以拦截的类型强制。