主持人注意:请抵制编辑代码或删除此通知的冲动。空白模式可能是问题的一部分,因此不应进行不必要的篡改。如果您处于“空白是无关紧要的”阵营,您应该能够接受代码。
在JavaScript中,(a==1&&a==2&&a==3)是否有可能求值为真?
这是一家大型科技公司提出的面试问题。事情发生在两周前,但我仍在努力寻找答案。我知道我们在日常工作中从未编写过这样的代码,但我很好奇。
主持人注意:请抵制编辑代码或删除此通知的冲动。空白模式可能是问题的一部分,因此不应进行不必要的篡改。如果您处于“空白是无关紧要的”阵营,您应该能够接受代码。
在JavaScript中,(a==1&&a==2&&a==3)是否有可能求值为真?
这是一家大型科技公司提出的面试问题。事情发生在两周前,但我仍在努力寻找答案。我知道我们在日常工作中从未编写过这样的代码,但我很好奇。
当前回答
它可以在全局范围内使用以下方法完成。对于nodejs,在下面的代码中使用global而不是window。
var值=0;Object.defineProperty(窗口,“a”{获取:函数(){返回++val;}});如果(a==1&&a==2&&a==3){console.log(“是”);}
这个答案通过定义getter来检索变量,从而滥用了全局范围在执行上下文中提供的隐式变量。
其他回答
使用代理:
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
与大多数其他答案一样,这只适用于松散的等式检查(==),因为严格的等式检查不执行代理可以拦截的类型强制。
这一个使用了带有良好副作用的defineProperty,导致全局变量!
变量_a=1Object.defineProperty(this,“a”{“获取”:()=>{返回_a++;},可配置:真});控制台日志(a)控制台日志(a)控制台日志(a)
令人惊讶的是,是的。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)
如果你曾经遇到过这样的面试问题(或者注意到代码中的一些同样意外的行为),想想什么样的事情可能会导致乍一看不可能的行为:
编码:在这种情况下,您正在查看的变量不是您认为的变量。如果您故意使用Unicode,使用同形符或空格字符使变量的名称看起来像另一个变量,则可能会发生这种情况,但也可能会意外地引入编码问题,例如,当从Web复制和粘贴包含意外Unicode码点的代码时(例如,因为内容管理系统进行了一些“自动格式化”,例如将fl替换为Unicode“LATIN小LIGATURE fl”(U+FB02))。竞争条件:可能会出现竞争条件,即代码未按开发人员预期的顺序执行的情况。竞争条件通常发生在多线程代码中,但多线程不是竞争条件的必要条件——异步就足够了(不要混淆,异步并不意味着在后台使用多个线程)。请注意,因此JavaScript也不能因为是单线程而不受竞争条件的限制。请参阅此处了解一个简单的单线程但异步的示例。然而,在单个语句的上下文中,在JavaScript中很难达到竞争条件。JavaScript与web工作者有点不同,因为您可以有多个线程@mehulmpt向我们展示了一个使用web工作者的概念证明。副作用:相等比较操作的副作用(不必像这里的示例那样明显,通常副作用非常微妙)。
这类问题可能出现在许多编程语言中,不仅仅是JavaScript,因此我们在这里没有看到经典的JavaScript WTF 1。
当然,面试问题和这里的样本看起来都很做作。但它们很好地提醒我们:
副作用会变得非常严重,一个精心设计的程序应该没有不必要的副作用。多线程和可变状态可能会有问题。不正确进行字符编码和字符串处理可能会导致严重的错误。
1例如,您可以在这里找到一个完全不同的编程语言(C#)中的示例,显示出副作用(一个明显的副作用)。
我认为这是实现它的最小代码:
i=0,a={valueOf:()=>++i}如果(a==1&&a==2&&a==3){console.log('Mind===Blown');}
创建具有自定义值Of的虚拟对象,该值在每次调用时递增全局变量i。23个字符!