主持人注意:请抵制编辑代码或删除此通知的冲动。空白模式可能是问题的一部分,因此不应进行不必要的篡改。如果您处于“空白是无关紧要的”阵营,您应该能够接受代码。
在JavaScript中,(a==1&&a==2&&a==3)是否有可能求值为真?
这是一家大型科技公司提出的面试问题。事情发生在两周前,但我仍在努力寻找答案。我知道我们在日常工作中从未编写过这样的代码,但我很好奇。
主持人注意:请抵制编辑代码或删除此通知的冲动。空白模式可能是问题的一部分,因此不应进行不必要的篡改。如果您处于“空白是无关紧要的”阵营,您应该能够接受代码。
在JavaScript中,(a==1&&a==2&&a==3)是否有可能求值为真?
这是一家大型科技公司提出的面试问题。事情发生在两周前,但我仍在努力寻找答案。我知道我们在日常工作中从未编写过这样的代码,但我很好奇。
当前回答
如果你曾经遇到过这样的面试问题(或者注意到代码中的一些同样意外的行为),想想什么样的事情可能会导致乍一看不可能的行为:
编码:在这种情况下,您正在查看的变量不是您认为的变量。如果您故意使用Unicode,使用同形符或空格字符使变量的名称看起来像另一个变量,则可能会发生这种情况,但也可能会意外地引入编码问题,例如,当从Web复制和粘贴包含意外Unicode码点的代码时(例如,因为内容管理系统进行了一些“自动格式化”,例如将fl替换为Unicode“LATIN小LIGATURE fl”(U+FB02))。竞争条件:可能会出现竞争条件,即代码未按开发人员预期的顺序执行的情况。竞争条件通常发生在多线程代码中,但多线程不是竞争条件的必要条件——异步就足够了(不要混淆,异步并不意味着在后台使用多个线程)。请注意,因此JavaScript也不能因为是单线程而不受竞争条件的限制。请参阅此处了解一个简单的单线程但异步的示例。然而,在单个语句的上下文中,在JavaScript中很难达到竞争条件。JavaScript与web工作者有点不同,因为您可以有多个线程@mehulmpt向我们展示了一个使用web工作者的概念证明。副作用:相等比较操作的副作用(不必像这里的示例那样明显,通常副作用非常微妙)。
这类问题可能出现在许多编程语言中,不仅仅是JavaScript,因此我们在这里没有看到经典的JavaScript WTF 1。
当然,面试问题和这里的样本看起来都很做作。但它们很好地提醒我们:
副作用会变得非常严重,一个精心设计的程序应该没有不必要的副作用。多线程和可变状态可能会有问题。不正确进行字符编码和字符串处理可能会导致严重的错误。
1例如,您可以在这里找到一个完全不同的编程语言(C#)中的示例,显示出副作用(一个明显的副作用)。
其他回答
面试规则一;永远不要说不可能。
不需要隐藏角色的诡计。
窗口__定义Getter__('a',函数(){if(类型i!=='number'){//在全局命名空间中定义i,以便在运行此函数后不会丢失i=0;}返回++i;});如果(a==1&&a==2&&a==3){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返回一个字符串,那么引擎将尝试将该字符串转换为一个数字,从而得到相同的最终结果,尽管路径稍长。
如果没有正则表达式就无法执行任何操作:
变量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,依此类推。
这是另一种变体,使用数组弹出所需的任何值。
常量a={n: [3,2,1],toString:函数(){return a.n.pop();}}如果(a==1&&a==2&&a==3){console.log(“是”);}
或者,您可以为其使用一个类,为检查使用一个实例。
函数A(){var值=0;this.valueOf=函数(){return++value;};}var a=新a;如果(a==1&&a==2&&a==3){console.log('bingo!');}
EDIT
使用ES6类时,情况如下
A类{构造器(){this.value=0;this.valueOf();}值Of(){返回this.value++;};}设a=新a;如果(a==1&&a==2&&a==3){console.log('bingo!');}