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

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

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


当前回答

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

不需要隐藏角色的诡计。

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

其他回答

令人惊讶的是,是的。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)

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

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

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

JavaScript

a==a+1

在JavaScript中,没有整数,只有数字,它们被实现为双精度浮点数。

这意味着如果一个数字a足够大,它可以被认为等于四个连续的整数:

a=100000000000000000如果(a==a+1&&a==a+2&&a==a+3){console.log(“精度损失!”);}

的确,这并不是面试官所要求的(当a=0时不起作用),但它不涉及隐藏函数或运算符重载的任何技巧。

其他语言

作为参考,Ruby和Python中有a==1&a==2&&a==3个解决方案。稍加修改,在Java中也是可能的。

Ruby

使用自定义==:

class A
  def ==(o)
    true
  end
end

a = A.new

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

或增加一个:

def a
  @a ||= 0
  @a += 1
end

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

蟒蛇

您可以为新类定义==:

class A:
    def __eq__(self, who_cares):
        return True
a = A()

if a == 1 and a == 2 and a == 3:
    print("Don't do that!")

或者,如果你喜欢冒险,重新定义整数的值:

import ctypes

def deref(addr, typ):
    return ctypes.cast(addr, ctypes.POINTER(typ))

deref(id(2), ctypes.c_int)[6] = 1
deref(id(3), ctypes.c_int)[6] = 1
deref(id(4), ctypes.c_int)[6] = 1

print(1 == 2 == 3 == 4)
# True

它可能会出错,这取决于您的系统/解释器。

python控制台与上述代码一起崩溃,因为2或3可能在后台使用。如果您使用不太常见的整数,则可以正常工作:

>>> import ctypes
>>> 
>>> def deref(addr, typ):
...     return ctypes.cast(addr, ctypes.POINTER(typ))
... 
>>> deref(id(12), ctypes.c_int)[6] = 11
>>> deref(id(13), ctypes.c_int)[6] = 11
>>> deref(id(14), ctypes.c_int)[6] = 11
>>> 
>>> print(11 == 12 == 13 == 14)
True

Java

可以修改Java Integer缓存:

package stackoverflow;

import java.lang.reflect.Field;

public class IntegerMess
{
    public static void main(String[] args) throws Exception {
        Field valueField = Integer.class.getDeclaredField("value");
        valueField.setAccessible(true);
        valueField.setInt(1, valueField.getInt(42));
        valueField.setInt(2, valueField.getInt(42));
        valueField.setInt(3, valueField.getInt(42));
        valueField.setAccessible(false);

        Integer a = 42;

        if (a.equals(1) && a.equals(2) && a.equals(3)) {
            System.out.println("Bad idea.");
        }
    }
}

这是可能的!

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

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

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

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

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

这是另一种变体,使用数组弹出所需的任何值。

常量a={n: [3,2,1],toString:函数(){return a.n.pop();}}如果(a==1&&a==2&&a==3){console.log(“是”);}