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

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

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


当前回答

实际上,在每种编程语言中,问题第一部分的答案都是“是”。例如,这是在C/C++的情况下:

#define a   (b++)
int b = 1;
if (a ==1 && a== 2 && a==3) {
    std::cout << "Yes, it's possible!" << std::endl;
} else {
    std::cout << "it's impossible!" << std::endl;
}

其他回答

这是可能的!

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

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

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

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

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

如果利用==的工作原理,您可以简单地创建一个带有自定义toString(或valueOf)函数的对象,该函数在每次使用时都会更改其返回的内容,以使其满足所有三个条件。

常量a={i: 1中,toString:函数(){返回a.i++;}}如果(a==1&&a==2&&a==3){console.log(“Hello World!”);}


这之所以有效,是因为使用了松散的相等运算符。使用松散相等时,如果其中一个操作数的类型与另一个不同,则引擎将尝试将其中一个转换为另一个。在左边是一个对象,右边是一个数字的情况下,它将尝试通过首先调用valueOf(如果它是可调用的)将对象转换为一个数字,否则,它将调用toString。在这种情况下,我习惯使用String,因为这是我想到的,valueOf更有意义。如果我从toString返回一个字符串,那么引擎将尝试将该字符串转换为一个数字,从而得到相同的最终结果,尽管路径稍长。

使用符号的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。

如果你曾经遇到过这样的面试问题(或者注意到代码中的一些同样意外的行为),想想什么样的事情可能会导致乍一看不可能的行为:

编码:在这种情况下,您正在查看的变量不是您认为的变量。如果您故意使用Unicode,使用同形符或空格字符使变量的名称看起来像另一个变量,则可能会发生这种情况,但也可能会意外地引入编码问题,例如,当从Web复制和粘贴包含意外Unicode码点的代码时(例如,因为内容管理系统进行了一些“自动格式化”,例如将fl替换为Unicode“LATIN小LIGATURE fl”(U+FB02))。竞争条件:可能会出现竞争条件,即代码未按开发人员预期的顺序执行的情况。竞争条件通常发生在多线程代码中,但多线程不是竞争条件的必要条件——异步就足够了(不要混淆,异步并不意味着在后台使用多个线程)。请注意,因此JavaScript也不能因为是单线程而不受竞争条件的限制。请参阅此处了解一个简单的单线程但异步的示例。然而,在单个语句的上下文中,在JavaScript中很难达到竞争条件。JavaScript与web工作者有点不同,因为您可以有多个线程@mehulmpt向我们展示了一个使用web工作者的概念证明。副作用:相等比较操作的副作用(不必像这里的示例那样明显,通常副作用非常微妙)。

这类问题可能出现在许多编程语言中,不仅仅是JavaScript,因此我们在这里没有看到经典的JavaScript WTF 1。

当然,面试问题和这里的样本看起来都很做作。但它们很好地提醒我们:

副作用会变得非常严重,一个精心设计的程序应该没有不必要的副作用。多线程和可变状态可能会有问题。不正确进行字符编码和字符串处理可能会导致严重的错误。

1例如,您可以在这里找到一个完全不同的编程语言(C#)中的示例,显示出副作用(一个明显的副作用)。

它可以在全局范围内使用以下方法完成。对于nodejs,在下面的代码中使用global而不是window。

var值=0;Object.defineProperty(窗口,“a”{获取:函数(){返回++val;}});如果(a==1&&a==2&&a==3){console.log(“是”);}

这个答案通过定义getter来检索变量,从而滥用了全局范围在执行上下文中提供的隐式变量。