(a == 1 && a == 2 && a == 3)可以评估为真吗?


2484

主持人注意:请不要编辑代码或删除此声明。空格模式可能是问题的一部分,因此不应不必要地对其进行篡改。如果您处于“空白无关紧要”的阵营中,则应该能够原样接受代码。

有可能用JavaScript (a== 1 && a ==2 && a==3)评估true吗?

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


9
评论不作进一步讨论;此对话已转移至聊天
deceze

109
对于显然投票赞成Cloae的人们来说,这太笼统了:是对Java语言的挖掘,说有太多有效答案吗?
tomsmeding

24
有些人围着思考什么是可能的。其他人则将精力集中在是否为客户构建可行的,业务正确的产品上。IMO,这个问题除了您切勿在面试中提出此类问题或编写此类代码外,没有任何实际用途。这就是为什么应该关闭它的原因。我的意思是,企业真的意识到他们付了一些人真钱来围坐谈论这些东西吗?
P.Brian.Mackey

15
阅读答案后,这个故事的寓意是:不要==在您的意思时使用===;具有禁止非ASCII变量名称的编码标准,并且具有执行前两种道德的整理过程。
Jesse C. Slicer

87
主持人注意: Stack Overflow的历史悠久,人们总是用不同语言的答案来回答问题。这些都是试图回答这个问题,因为他们是一般问题的解决方案,虽然在不同的语言。请不要将其标记为“不是答案”。话虽这么说,也请不要发布更多使用不同语言的答案-正如其他一些答案中的注释所指出的那样,该问题特定于JavaScript是有原因的,还有一个原因是我们喜欢我们特定于语言的问题保持如此。
BoltClock

Answers:


3323

如果你趁如何==工作,你可以简单地创建一个自定义的对象toString(或valueOf改变它使用它,使得它满足所有这三个条件,每次返回)功能。

const a = {
  i: 1,
  toString: function () {
    return a.i++;
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}


之所以起作用,是因为使用了松散的相等运算符。使用宽松相等时,如果其中一个操作数的类型不同于另一个,则引擎将尝试将一个转换为另一个。如果对象在左侧,数字在右侧,则它将尝试通过首先调用对象(valueOf如果可调用)将其转换为数字,否则将调用toStringtoString在这种情况下,我之所以使用它,仅仅是因为它浮现在脑海,valueOf将变得更有意义。如果我从中返回了一个字符串toString,则引擎将尝试将该字符串转换为一个数字,使我们得到相同的最终结果,尽管路径略长。


70
您可以通过更改隐式valueOf()操作来实现此目的吗?
英镑弓箭手

43
是的,出于相同的原因,valueOf可以代替toString起作用
Kevin B,

4
评论不作进一步讨论;此对话已转移至聊天
deceze

13
根据一数量的转换将首先尝试了valueOf稍微好一点。
Salman A

6
@Pureferret等式比较的左侧是一个对象,而不是数字。该对象具有number属性i不会干扰引擎。;)
tomsmeding

2057

我无法抗拒-其他答案无疑是正确的,但是您真的无法跳过以下代码:

var a = 1;
var a = 2;
var a = 3;
if(aᅠ==1 && a== 2 &&ᅠa==3) {
    console.log("Why hello there!")
}

注意if语句中奇怪的间隔(我从您的问题中复制了该间隔)。这是半角的韩文(对于不熟悉的人来说是韩文),它是Unicode空格字符,不会被ECMA脚本解释为空格字符-这意味着它是标识符的有效字符。因此,存在三个完全不同的变量,一个变量在a后面是韩文,一个变量在a之前,最后一个是a。_为了便于阅读而替换了该空间,相同的代码如下所示:

var a_ = 1;
var a = 2;
var _a = 3;
if(a_==1 && a== 2 &&_a==3) {
    console.log("Why hello there!")
}

在Mathias的变量名称验证器上检查验证。如果他们的问题中确实包含了这个怪异的空格,我相信这暗示了这种答案。

不要这样 说真的

编辑:这已经到了我的注意,(虽然不是允许启动一个变量)的零宽度木匠零宽不连字字符也允许在变量名-看到模糊处理JavaScript和零角字符-利弊?

如下所示:

var a= 1;
var a‍= 2; //one zero-width character
var a‍‍= 3; //two zero-width characters (or you can use the other one)
if(a==1&&a‍==2&&a‍‍==3) {
    console.log("Why hello there!")
}


368
从原始问题中的奇数间距来看,我认为这恰恰是面试问题所寻找的答案-利用看起来像空格的非空格字符。好地方!
Baracus

18
@Baracus是RonJohn在他对Kevin的回答的评论中注意到了奇怪的间隔,这使我想起了这种(糟糕的)技术,因此我无法发现它。几年前,有人在某处发表博文,使我感到困惑,但我还没有人回答这个问题,我有点以为这是现在的常识。
杰夫(Jeff)

102
当然,这被禁止为标准漏洞,也适用于采访。[需要引用]
Sanchises

13
考虑到原始间距,可能会更糟,即使用了一个变量var ᅠ2 = 3。因此,存在三个变量aᅠᅠ= 1, ᅠ2 = 3, a = 3a␣ = 1, ␣2 = 3, a = 3,使得(a␣==1 && a==␣2 && a==3))…
Holger

2
@ AL-zami有两个变量,这说明你的屏幕空间上的一个额外字符,但被解释为标识符的一部分,这意味着有三个独立的变量- 一,A和A -额外的字符朝鲜文半角空格。
杰夫

620

有可能的!

var i = 0;

with({
  get a() {
    return ++i;
  }
}) {
  if (a == 1 && a == 2 && a == 3)
    console.log("wohoo");
}

这在with语句内部使用了一个吸气剂,可以a求出三个不同的值。

...这仍然不意味着应该在真实代码中使用...

更糟糕的是,该技巧也可以与配合使用===

  var i = 0;

  with({
    get a() {
      return ++i;
    }
  }) {
    if (a !== a)
      console.log("yep, this is printed.");
  }


65
是的,我正在尝试同一件事:)因此,面试中正确的答案是:“ 由于我从未使用过,所以它不会在我的代码中发生with”。
尖尖的

7
@Pointy-而且,我在with不允许的严格模式下编程。
jfriend00

6
@Pointy在接受的答案中,他们做了类似的事情而没有with这样做,所以它可能发生
Jungkook

2
@jorrit没有人会使用==。并===阻止接受的答案
Jonas Wilms

4
@JonasW。许多人仍在使用,==with此后我再也没有看到过……实际上,从来没有在JS文档中写过“请不要使用”。无论如何,一个不错的解决方案。
wortwart

516

没有吸气剂或valueOf的示例:

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

这工作,因为==所调用toString它调用.join的阵列。

另一个解决方案,使用Symbol.toPrimitive的等效于ES6 toString/valueOf

let i = 0;
let a = { [Symbol.toPrimitive]: () => ++i };

console.log(a == 1 && a == 2 && a == 3);


9
without valueOf,好吧...它是更间接的,但基本上是相同的。
乔纳斯·威尔姆斯

11
我真的很喜欢这种解决方案,因为除了对象拥有连接功能之外,您什么都不覆盖,而且这是一种非常简洁易读的hack,可以使逻辑评估为真。
Alex Pedersen

28
老实说,我认为这是最好的答案。它不涉及任何异常,只需设置一些值即可。即使具有基本的JS知识也很容易理解。做得好。
扎克·德尔文塔尔

14
这非常有意义,几乎感觉有用。
安德鲁

7
我知道大多数答案都是关于滥用的toStringvalueOf但是这个让我完全措手不及。非常聪明,我不知道它在.join内部调用过,但这很有意义。
GBarroso

268

如果询问是否可能(不是必须),则可以询问“ a”以返回随机数。如果它顺序生成1、2和3,那将是正确的。

with({
  get a() {
    return Math.floor(Math.random()*4);
  }
}){
  for(var i=0;i<1000;i++){
    if (a == 1 && a == 2 && a == 3){
      console.log("after " + (i+1) + " trials, it becomes true finally!!!");
      break;
    }
  }
}


102
即使我知道其他解决方案,我也会故意给出此答案,因为它可以回答问题,但显然不是他们所追求的。玩愚蠢的游戏,赢得愚蠢的奖品。
ESR

2
但是,如果要进行1000多次试验,该怎么办?
Piyin

9
@Piyin如果需要进行1000多次试用,您将赢得大奖!
Skeets

5
我喜欢这个答案,因为它考虑到了极点表明,这是可能的任何语言,如果该程序运行时CPU的寄存器/缓存中被击中有足够的宇宙射线,或者如果硬要把执行电源故障,使得故障分支如果有条件的话实际上不会跳转。
Ponkadoodle

最低:1,最高:
412。– KyleFairns

210

如果没有正则表达式您无法做任何事情:

var a = {
  r: /\d/g, 
  valueOf: function(){
    return this.r.exec(123)[0]
  }
}

if (a == 1 && a == 2 && a == 3) {
    console.log("!")
}

之所以起作用valueOf,是因为将Object与原始值(例如Number)进行比较时会调用自定义方法。主要技巧是a.valueOf每次都返回新值,因为它正在调用exec带有g标志的正则表达式,这会导致lastIndex每次找到匹配项时都会更新该正则表达式。因此,第一次this.r.lastIndex == 0,它匹配1并更新lastIndexthis.r.lastIndex == 1,因此下次正则表达式将匹配2,依此类推。


22
@Abdillah的正则表达式对象会记住它匹配的最后一个索引,exec再次调用将从该索引开始搜索。MDN不太清楚。
西蒙·陈

我知道了,所以this.r正则表达式对象记住了状态/索引。谢谢!
Abdillah

我建议exec尽管将一个字符串传递给,而不是一个要字符串化的整数。
Bergi

使用正则表达式,现在您有两个问题
Aleksey Solovey '18年

191

可以在全局范围内使用以下内容来实现。为了nodejs使用global,而不是window在下面的代码。

var val = 0;
Object.defineProperty(window, 'a', {
  get: function() {
    return ++val;
  }
});
if (a == 1 && a == 2 && a == 3) {
  console.log('yay');
}

该答案通过定义获取程序以获取变量来滥用执行范围中全局范围提供的隐式变量。


假定它a是一个this似乎没有的属性。如果a是局部变量(看起来像),那么它将无法正常工作。
jfriend00

1
@ jfriend00你的意思是如果你放了var a; 某处?
jontro '18

是的 引用a == 1隐含a着某个地方的变量,而不是的属性this。尽管在全局变量之类的怪异地方都可能是正确的,但通常来说,用var a或声明变量let a并不意味着this您可以a像假定的代码那样访问属性。因此,您的代码显然假定了一些奇怪的全局变量。例如,您的代码无法在node.js中运行,也不在函数内部的严格模式下运行。您应该指定它起作用的确切环境,并可能解释它起作用的原因。否则,将产生误导。
jfriend00

@ jfriend00很好。不确定与其他已经回答的方法相结合会增加更多的价值。将更新答案
jontro

14
问题是,这种“永远”是真的吗?答案是肯定的,这就是其中一种可能的情况:a不是局部变量,而是在全局范围内使用递增的吸气剂定义。
扎克·德尔文塔尔

190

如果有a2个Web工作者通过SharedArrayBuffer以及一些主脚本访问变量,则可以这样做。的可能性较低,但有可能的是,当代码被编译成机器代码,网络工作者更新变量a只是在时间上的条件a==1a==2a==3得到满足。

这可以是由Web worker和JavaScript中的SharedArrayBuffer提供的多线程环境中竞争条件的示例。

这是上面的基本实现:

main.js

// Main Thread

const worker = new Worker('worker.js')
const modifiers = [new Worker('modifier.js'), new Worker('modifier.js')] // Let's use 2 workers
const sab = new SharedArrayBuffer(1)

modifiers.forEach(m => m.postMessage(sab))
worker.postMessage(sab)

worker.js

let array

Object.defineProperty(self, 'a', {
  get() {
    return array[0]
  }
});

addEventListener('message', ({data}) => {
    array = new Uint8Array(data)
    let count = 0
    do {
        var res = a == 1 && a == 2 && a == 3
        ++count
    } while(res == false) // just for clarity. !res is fine
    console.log(`It happened after ${count} iterations`)
    console.log('You should\'ve never seen this')
})

修饰符

addEventListener('message' , ({data}) => {
    setInterval( () => {
        new Uint8Array(data)[0] = Math.floor(Math.random()*3) + 1
    })
})

在我的MacBook Air上,第一次尝试经过约100亿次迭代后,它才发生:

在此处输入图片说明

第二次尝试:

在此处输入图片说明

就像我说的那样,机会很少,但是如果有足够的时间,它将达到条件。

提示:如果您的系统花费的时间太长。仅尝试a == 1 && a == 2并更改Math.random()*3Math.random()*2。在列表中添加越来越多的内容会降低命中率。


50
老实说,这是最好的答案。所有其他答案都需要刻意尝试做一些非常不直观的事情。这个答案实际上反映了现实世界中可能发生的某些情况-比赛条件。
汤姆·史威利

34
不仅如此-我实际上已经在现实世界中看到了这种情况。问题中没有确切的条件,但是肯定要在函数开始时检查(a == 1),然后在函数中检查(a == 2),并让代码同时满足这两个条件。仅供参考,我第一次看到这种情况发生在汽车发动机控制器中,我们制定了编码标准。第二次是在军用飞机的糠ff和火炬分配器系统中,在公司的第一天,我发现了这个问题并加以修复,而其他团队仍在讨论问题。(荣誉级别:高!:)
Graham

38
因此,您从事过使用网络工作者用javascript编程的“汽车控制器”和“糠aff和火炬喷洒器系统”的工作吗?我不认为我会再去外面。
psaxton

12
@psaxton :)当然不是-但是我们有共享数据的多线程软件。这是所有多线程软件的反模式,并非特定于Javascript或Web Worker。不管您使用汇编语言,Brainf * ck,Visual BASIC,C还是Javascript编程-如果在多线程应用程序中使用共享数据执行此操作,它将始终失败。
格雷厄姆

4
我认为这是@jontro答案的精心包装。
qntm

148

也可以使用一系列自我覆盖的getter:

(这类似于jontro的解决方案,但不需要计数器变量。)

(() => {
    "use strict";
    Object.defineProperty(this, "a", {
        "get": () => {
            Object.defineProperty(this, "a", {
                "get": () => {
                    Object.defineProperty(this, "a", {
                        "get": () => {
                            return 3;
                        }
                    });
                    return 2;
                },
                configurable: true
            });
            return 1;
        },
        configurable: true
    });
    if (a == 1 && a == 2 && a == 3) {
        document.body.append("Yes, it’s possible.");
    }
})();


61
请注意,使用吸气剂的方法也可以使用===,而不仅仅是==
Makyen

该解决方案依赖于this箭头函数体内的全局对象。
罗伊·廷克

@Midnightas我不会将其他答案归为“金字塔代码”
Patrick Roberts

请注意,这也适用于任意顺序,不是吗?喜欢(a == 3 && a == 2 && a == 1)吗?
约翰内斯

131

或者,您可以使用一个类,并使用一个实例进行检查。

function A() {
    var value = 0;
    this.valueOf = function () { return ++value; };
}

var a = new A;

if (a == 1 && a == 2 && a == 3) {
    console.log('bingo!');
}

编辑

使用ES6类,它看起来像这样

class A {
  constructor() {
    this.value = 0;
    this.valueOf();
  }
  valueOf() {
    return this.value++;
  };
}

let a = new A;

if (a == 1 && a == 2 && a == 3) {
  console.log('bingo!');
}


5
function A() {value = 0;开始?
Dave C

valueOf被覆盖,this method is usually called automatically by JavaScript behind the scenes, and not explicitly in code因此当我们比较该值时,它实际上会增加a ..
Danyal Sandeelo

130

我没有看到这个答案已经发布,所以我也将这个答案也加入了。这类似于Jeff对半角Hangul空间的回答

var a = 1;
var  = 2;
var а = 3;
if(a == 1 &&  == 2 && а == 3) {
    console.log("Why hello there!")
}

您可能会注意到与第二个略有差异,但是第一个和第三个与肉眼相同。所有3个字符都是不同的:

a-拉丁小写字母A-
全角拉丁小写字母A-
а西里尔字母小写字母A

它的通用术语是“象形文字”:看起来相同的不同unicode字符。通常很难获得完全无法区分的三个,但是在某些情况下您会很幸运。A,Α,А和Ꭺ会更好地工作(分别为拉丁字母A,希腊字母Alpha西里尔字母A切诺基A;不幸的是,希腊字母和切诺基小写字母与拉丁字母的区别太大aα,,所以对上述代码片段无济于事)。

这里有一整类的象形文字攻击,最常见的是假域名(例如wikipediа.org(西里尔字母)对wikipedia.org(拉丁字母)),但它也可以显示在代码中。通常称为不熟练的人(如评论中提到的,[ 不熟练的人]问题现在在PPCG不合时宜,但曾经是一种挑战,其中这类事情会出现)。我使用该网站找到用于此答案的同形文字。


19
我不会这样称呼“轻微差异”

4
@hvd完全取决于您的字体渲染。这就是我所看到的
Draco18s不再信任SE

1
@Jake是的,全角拉丁小写字母A并不是最大的象形文字(但大写字母变体很惊人)。通常,尽管您只需要两个即可获得理想的效果。
Draco18s不再信任

@ Draco18s同意re:通常只需要2个。额外信息也不错!
JakeSteam

10
您也可以使用Unicode变体选择器(U + FE00..U + FE0F)。这些都不是aa︀ a︁ a︂。无需再担心差异。
Salman A

108

是的,有可能!😎

»JavaScript

if‌=()=>!0;
var a = 9;

if‌(a==1 && a== 2 && a==3)
{
    document.write("<h1>Yes, it is possible!😎</h1>")
}

上面的代码是一个简短的版本(感谢@Forivin在注释中的注释),下面的代码是原始的:

var a = 9;

if‌(a==1 && a== 2 && a==3)
{
    //console.log("Yes, it is possible!😎")
    document.write("<h1>Yes, it is possible!😎</h1>")
}

//--------------------------------------------

function if‌(){return true;}

如果您只是看到我的代码的顶部并运行它,那么您会说WOW,怎么办?

所以我认为只要说“ 是”就足够了,可能有人对你说:没有什么是不可能的

技巧:我之后使用了一个隐藏字符if来创建一个函数,该函数的名称类似于if。在JavaScript中,我们无法覆盖关键字,因此我不得不使用这种方式。这是假的if,但在这种情况下对您有用!


» C#

我也写了一个C#版本(增加了属性值技术):

static int _a;
public static int a => ++_a;

public static void Main()
{
    if(a==1 && a==2 && a==3)
    {
        Console.WriteLine("Yes, it is possible!😎");
    }
}

现场演示


56
javascript版本是一种真正的危害人类罪,具有这样做的能力,应由联合国公约视为非法。我认为现在是时候清除所有Javacript知识了。
清晰的

2
函数声明可能更短。if‌=()=>!0
Forivin

4
为什么在地球上使用document.write?无论答案如何,这都是不招人的肯定方法。
Cerbrus

3
@Cerbrus,谢谢您的来信。我首先用写下了答案,console.log但将其更改为document.write。实际上console.log,我总是在代码中使用,但在这里我只想在StackOverflow代码段框中向用户显示文本。因此,我想展示自己的信息比生成的信息更优美console.log。单击Run Code Snippet我的答案和其他答案上的按钮。SO代码段允许我使用html以及JS和CSS,然后我想在答案中使用它并使其美观。我认为它没有任何负面影响,也没有使我的回答过大或过于复杂。
RAM

1
@更清晰,如果联合国公约能够有效地改变世界,那么我们应该拥有一个比这更好的世界。我们需要的不仅仅是联合国的声明,在这一天之前,我认为我们可以使用我的Javascript技巧;)
RAM

97

的JavaScript

a == a +1

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

这意味着,如果Number a足够大,则可以认为它等于三个连续的整数:

a = 100000000000000000
if (a == a+1 && a == a+2 && a == a+3){
  console.log("Precision loss!");
}

是的,这与面试官的要求不完全相同(与 a=0),但是它没有涉及隐藏函数或运算符重载的任何技巧。

其他语言

作为参考,有a==1 && a==2 && a==3Ruby和Python 中的解决方案。稍加修改,就可以在Java中实现。

红宝石

带有自定义==

class A
  def ==(o)
    true
  end
end

a = A.new

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

或增加a

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!")

爪哇

可以修改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.");
        }
    }
}

27
@cᴏʟᴅsᴘᴇᴇᴅ:Java,Javascript,potayto,potahto :)已经有足够好的JS答案了。我只是认为展示如何用其他语言完成它会很有趣,并且可能会给JS开发人员一些想法。
埃里克·杜米尼尔

2
@cᴏʟᴅsᴘᴇᴇᴅ:已更新为JS示例。
埃里克·杜米尼尔

1
为什么Java版本Integer a = 42不兼容(或不兼容)?据我了解,自动装箱Integer a = 42; a == 1 && a == 2 && a == 3应该将所有int 装箱。还是将此框拆开以进行比较?
CAD97

@ CAD97:Integer == int似乎导致取消装箱。但是使用Integer#equals(int)强制自动装箱,因此可以正常工作。感谢您的评论!
Eric Duminil

@StephanBijzitter:请解释。据我所知,只有NumbersJS,基本上像doubles。它们看起来像整数,您可以像整数一样使用它们,但是它们仍然不是整数。我认为n == n + 1Java / Python / C / Ruby / ...中的整数不可能永远都是正确的
Eric Duminil

80

这是一个倒置的版本@杰夫的回答 *其中隐藏字符(U + 115F,U + 1160或U + 3164),用于创建变量的样子123

var  a = 1;
var 1 = a;
var 2 = a;
var 3 = a;
console.log( a ==ᅠ1 && a ==ᅠ2 && a ==ᅠ3 );

*通过使用零宽度非连接器(U + 200C)和零宽度连接器(U + 200D)可以简化答案。这两个字符都可以在标识符中使用,但不能在开头使用:

var a = 1;
var a = 2;
var a = 3;
console.log(a == 1 && a == 2 && a == 3);

/****
var a = 1;
var a\u200c = 2;
var a\u200d = 3;
console.log(a == 1 && a\u200c == 2 && a\u200d == 3);
****/

使用相同的想法也可以使用其他技巧,例如,使用Unicode变体选择器创建看起来完全相似的变量(a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true)。


75

采访规则第一;永远不要说不可能。

无需隐藏角色欺骗。

window.__defineGetter__( 'a', function(){
    if( typeof i !== 'number' ){
        // define i in the global namespace so that it's not lost after this function runs
        i = 0;
    }
    return ++i;
});

if( a == 1 && a == 2 && a == 3 ){
    alert( 'Oh dear, what have we done?' );
}


6
哎哟。__defineGetter__实际上不是js语言的一部分,只是的丑陋版本definePropertytypeof不是一个函数,这个未声明的函数i很糟糕。似乎仍然值得40个投票:/
Jonas Wilms

6
@JonasW。41赞成:-)我知道__defineGetter__每个developer.mozilla.org/en-US/docs/Web/JavaScript/Reference / ... 都已弃用它,但是它显然在FireFox v 57.0.4中执行,所以我选择显示它而不是defineProperty()因为旧版代码是真实的,因此不能忽略。不管丑陋,i以我的方式进行宣告都是众所周知的/有记录的行为。也许我只是在PCG心情中\\ _(ツ)_ /
¯– MonkeyZeus

68

坦白说,不管是否有一种方法可以评估它是否为真(正如其他人所展示的那样,有多种方法),我作为一个进行过数百次采访的人所寻找的答案是类似于以下内容:

“好吧,也许是的,在某些奇怪的情况下,这些情况对我来说并不是立即显而易见的……但是,如果我在真实代码中遇到了这种情况,那么我将使用常见的调试技术来弄清楚它的工作方式以及原因然后立即重构代码以避免出现这种情况……但更重要的是:我绝对不会一开始就编写该代码,因为那是卷积代码的定义,我将努力永远不要编写卷积代码。”

我想有些面试官会冒犯明显被认为是一个非常棘手的问题的行为,但是我不介意有观点的开发人员,尤其是当他们可以用理性的思想支持并可以将我的问题与之吻合时关于自己的有意义的陈述。


13
这个问题(或所有面试问题)可能是测试候选人考虑问题的意愿,尤其是像这样“明显”的问题。拒绝思考的人是因为他们相信自己“知道”答案并不是一个好选择。
洗发水

5
@Don Hatch不,如果他们以真诚的态度回答他们,特别是如果他们像其他人一样给出了正确答案,我不会对他们进行惩罚……但是我会要求跟进以尝试调查他们是否认为这是正确的。是否编写代码的好方法。知识渊博并能够提出“正确”的答案只是成为优秀开发人员的一部分。对于“专业”开发人员而言,更重要的是编写在开发过程中易于理解和维护的代码,通常是能力较弱的开发人员使用的代码。过于聪明的开发人员与没有能力的IME一样糟糕。
Frank W. Zammetti

16
这不能回答问题。
TylerH '18

6
关于这个答案的可悲的事情是,一个1rep用户昨天回答了这个问题,并得到2张反对票,导致他删除了这个问题。
乔纳斯·威尔姆斯

8
@JohnColeman问题询问代码如何评估为true。它并没有询问面试官首先提出这个问题的原因。这个答案甚至没有试图解决所要提出的问题,而是完全侧重于“我会做什么”的尝试,以猜测面试官的目的是什么。如果有人提出这个问题,那将是太广泛了。因此,此答案不属于此处或网站上的任何地方。
TylerH

43

如果您遇到这样的面试问题(或注意到您的代码中有一些同样意想不到的行为),请考虑一下什么样的事情可能导致看起来乍看之下是不可能的行为:

  1. 编码:在这种情况下,您正在查看的变量不是您认为的变量。如果您故意使用象形文字空格字符来弄乱Unicode,以使变量名看起来像另一个变量,则可能会发生这种情况,但是编码问题也可能会意外引入,例如,从Web复制和粘贴包含意外Unicode代码的代码时,点(例如,因为内容管理系统做了一些“自动格式化”,例如fl用Unicode“拉丁文小写本FL”(U + FB02)代替)。

  2. 竞争条件:可能发生竞争条件,即未按照开发人员期望的顺序执行代码的情况。竞态条件通常发生在多线程代码中,但是竞态条件才可能需要多个线程–异步性就足够了(不要感到困惑,异步并不意味着在后台使用了多个线程)。

    请注意,因此JavaScript也不仅仅因为它是单线程而摆脱了竞争条件。有关简单的单线程但异步的示例,请参见此处。但是,在单个语句的上下文中,使用JavaScript很难达到竞争条件。

    网络工作者使用的JavaScript有所不同,因为您可以拥有多个线程。@mehulmpt向我们展示了使用网络工作者的绝佳概念证明

  3. 副作用:相等比较操作的副作用(不必像此处的示例中那样明显,副作用通常非常微妙)。

这些类型的问题可以出现在很多编程语言,不仅JavaScript,因此我们没有看到经典的一个JavaScript的WTFs这里1

当然,面试问题和这里的样本看起来都是非常人为的。但它们很好地提醒您:

  • 副作用可能会变得非常令人讨厌,并且精心设计的程序应避免不必要的副作用。
  • 多线程和可变状态可能会出现问题。
  • 不正确地进行字符编码和字符串处理会导致令人讨厌的错误。

1举例来说,你可以找到在一个完全不同的编程语言(C#)的例子显示出副作用(一个明显的例子)在这里


1
然后,问题变得太广泛了。不同的语言可以轻松地实现此目的。这个问题吸引了很多关注,因为这是针对JS的问答,但这只是我的2c。
cs95

1
原因是C#和javascript不同,所以此答案不合理。
埃德温

3
@Edwin:原因完全相同:Unicode出现类似外观的字形或空格字符,竞争条件或比较操作的副作用(后者在我的示例中显示)。
德克·沃尔玛(Birk Vollmar)'18年

2
@cᴏʟᴅsᴘᴇᴇᴅ:有时从更广阔的角度看待事物有助于发现实际问题。
德克·沃尔玛(Birk Vollmar)'18年

3
我希望这个答案可以某种“元”的方式标记到这个问题上。阅读完所有上面的答案后,我被留下的感觉就像JS有这么多洞,但你只是总结了所有的答案中一气呵成。在我看来,您这样做的方式使这成为一个出色的面试问题(如果删除了特定于语言的标签)。太棒了!
KCE

41

这是另一个变体,使用数组弹出您想要的任何值。

const a = {
  n: [3,2,1],
  toString: function () {
    return a.n.pop();
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Yes');
}


31

好的,还有发电机的另一个技巧:

const value = function* () {
  let i = 0;
  while(true) yield ++i;
}();

Object.defineProperty(this, 'a', {
  get() {
    return value.next().value;
  }
});

if (a === 1 && a === 2 && a === 3) {
  console.log('yo!');
}


您说的是hack,但我很确定这是生成器的用例...:)(嗯,除了这依赖于this作为窗口对象)
Cody G

29

使用代理

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

代理基本上伪装成目标对象(第一个参数),但是拦截对目标对象的操作(在本例中为“获取属性”操作),因此有机会进行默认对象行为以外的操作。在这种情况下,a==强制其类型以将其与每个数字进行比较时,将调用“获取属性”操作。有时候是这样的:

  1. 我们创建一个目标对象,{ i: 0 }其中的i属性是我们的计数器
  2. 我们为目标对象创建一个代理并将其分配给 a
  3. 对于每次a ==比较,a的类型都被强制为原始值
  4. 这种类型的强制导致a[Symbol.toPrimitive]()内部调用
  5. 代理a[Symbol.toPrimitive]使用“获取处理程序” 拦截获取功能
  6. 代理的“获取处理程序”检查要获取的属性是否为Symbol.toPrimitive,在这种情况下,该属性会递增,然后从目标对象返回计数器++target.i。如果要检索其他属性,我们将退回到返回默认属性值,target[name]

所以:

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

与大多数其他答案一样,这仅适用于宽松的相等性检查(==),因为严格的相等性检查(===)不会键入代理可以拦截的强制性。


2
但是,为此使用代理是没有意义的- Symbol.toPrimitive在对象上以相同的方式定义同样可以工作。
Ry-

27

实际上,对于每种编程语言,问题第一部分的答案都是“是”。例如,在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;
}

27
我不认为每种编程语言都有可能。例如,并非所有语言都具有预处理器。因此,并非所有语言都使用&&逻辑“和”。
Keith Thompson

3
我发现了一种在PythonC ++中都可以使用运算符重载的方法。
唐老鸭

7
您可以在Java中通过使用反射并弄乱整数缓存来做到这一点。
CAD97

7
无法使用不支持该点突变的语言来做到这一点,例如haskell中没有可比的东西
Jason Carr

4
问题是关于JavaScript而不是C ++的问题。
所有工人都

26

相同,但不同,但仍相同(可以“测试”多次):

const a = { valueOf: () => this.n = (this.n || 0) % 3 + 1}
    
if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}

我的想法从Number对象类型方程的工作原理开始。


4
也第二次工作!
Salman A

25

使用Symbols的ECMAScript 6答案:

const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));

由于==使用,JavaScript是应该裹胁a到了接近第二个操作数(123在这种情况下)。但是在JavaScript尝试自行计算强制之前,它会尝试调用Symbol.toPrimitive。如果提供Symbol.toPrimitiveJavaScript,则将使用您的函数返回的值。如果没有,JavaScript将调用valueOf


24

我认为这是实现它的最少代码:

i=0,a={valueOf:()=>++i}

if (a == 1 && a == 2 && a == 3) {
  console.log('Mind === Blown');
}

使用自定义创建虚拟对象,该自定义对象将在每次调用时valueOf增加全局变量i。23个字符!


14

这使用了带有引起全局变量的副作用的defineProperty!

var _a = 1

Object.defineProperty(this, "a", {
  "get": () => {
    return _a++;
  },
  configurable: true
});

console.log(a)
console.log(a)
console.log(a)


8
您可以在以下位置使用闭包aget: (a => () => ++a)(0),无需全局。
Nina Scholz

13
@NinaScholz当然,但是我们这里谈论的是不良做法-让我来看看:D
Ben Aubin

1

通过覆盖valueOf类声明,可以完成以下操作:

class Thing {
    constructor() {
        this.value = 1;
    }

    valueOf() {
        return this.value++;
    }
}

const a = new Thing();

if(a == 1 && a == 2 && a == 3) {
    console.log(a);
}

发生的事情是valueOf在每个比较运算符中调用的。在第一个上,a将等于1,在第二个上,a将等于2,依此类推,依此类推,因为每次valueOf都调用时,的值a会增加。

因此console.log将触发并输出(无论如何在我的终端中)Thing: { value: 4},表明条件为true。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.