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

Dim*_*dha 2438 javascript ecmascript-6

主持人注意:请拒绝编辑代码或删除此通知的冲动.空白模式可能是问题的一部分,因此不应该被不必要地篡改.如果你在"空白是微不足道的"阵营,你应该能够接受原样.

是否有可能在JavaScript中(a== 1 && a ==2 && a==3)进行评估true

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

use*_*654 3282

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

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

if(a == 1 && a == 2 && a == 3) {
  console.log('Hello World!');
}
Run Code Online (Sandbox Code Playgroud)


这种方法的原因是由于使用了松散的等式运算符.当使用松散相等时,如果其中一个操作数的类型与另一个不同,则引擎将尝试将一个操作数转换为另一个.如果左边是一个对象而右边是一个数字,它会尝试通过首先调用valueOf它来将对象转换为数字(如果它是可调用的),如果失败,它将调用toString.我toString在这种情况下使用的仅仅是因为它是我想到的,valueOf会更有意义.如果我改为返回一个字符串toString,那么引擎会尝试将字符串转换为给出相同结果的数字,但路径稍长.

  • 你能通过修改隐含的`valueOf()`操作来实现这个目的吗? (68认同)
  • 是的,valueOf代替toString也是出于同样的原因 (42认同)
  • 根据[this](http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8),首先尝试进行数字转换,因此`valueOf`稍好一些. (13认同)
  • @Pureferret左侧的相等比较是一个对象,而不是一个数字.那个对象在`i`上有一个数字属性不会打扰引擎.;) (6认同)
  • 评论不适用于扩展讨论; 这个对话已经[转移到聊天](http://chat.stackoverflow.com/rooms/163229/discussion-on-answer-by-kevin-b-in-javascript-how-can-you-have-the-声明-IF). (4认同)
  • 幸运的是,没有. (2认同)
  • 为什么不使用**`this`**?ie:`toString:function(){return this.i ++; }` (2认同)
  • @rkosegi没有,它在js中是不可能的.代码的行为与规范中的定义相同,即使已经过优化 (2认同)
  • @zxxc不,数字和`数字`不同(一个是基元,一个是对象,所以它们会立即失败严格`===`比较).数字原语没有构造函数(也没有任何原语). (2认同)
  • 只是一个总是保持条件的补充:`const a = {i:0,toString:function(){var returns =(++ ai); if(ai> = 3){ai = 0; } return返回; } (2认同)

Jef*_*eff 2031

我无法抗拒 - 其他答案无疑是正确的,但你真的无法超越以下代码:

var a? = 1;
var a = 2;
var ?a = 3;
if(a?==1 && a== 2 &&?a==3) {
    console.log("Why hello there!")
}
Run Code Online (Sandbox Code Playgroud)

请注意if语句中的奇怪间距(我从您的问题中复制).它是半角度Hangul(对于那些不熟悉的人来说是朝鲜语),它是一个Unicode空格字符,ECMA脚本不将其解释为空格字符 - 这意味着它是标识符的有效字符.因此,有三个完全不同的变量,一个是在a之后的韩文,一个是之前的,另一个只是一个._为了便于阅读,替换空格,相同的代码如下所示:

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

查看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!")
}
Run Code Online (Sandbox Code Playgroud)

  • 从原问题中的奇数间距来判断,我认为这正是面试问题所寻求的答案 - 利用看起来像空格的非空格字符.好点! (366认同)
  • 当然,这被禁止作为[标准漏洞](https://codegolf.meta.stackexchange.com/a/1657/32352),这也适用于访谈.[引证需要] (102认同)
  • @Baracus RonJohn注意到他对Kevin的答案中的奇怪间距让我想起了这种(糟糕的)技巧,所以我不能因为发现它而受到赞扬.我有点惊讶没有人已经回答过这个问题,因为几年前由于某个博客文章而在我的工作中走了一段时间 - 我有点认为这是非常常见的知识. (18认同)
  • 考虑到原始间距,可能更糟糕,即使用变量`varㅤ2= 3`; 所以有三个变量`aㅤㅤ= 1,ㅤ2= 3,a = 3`(`a␣= 1,␣2= 3,a = 3`,所以`(a␣== 1 && a ==␣2&& a == 3)`)... (13认同)
  • @ AL-zami在两个变量中有一个额外的字符,它在屏幕上显示为一个空格,但被解释为标识符的一部分,这意味着有三个独立的变量--a,a和a_ - 额外的字符是韩文半宽空间. (2认同)

Jon*_*lms 608

有可能的!

var i = 0;

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

这在with语句中使用getter 来a评估三个不同的值.

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

更糟糕的是,这个技巧也适用于===.

  var i = 0;

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

  • 是的,我正在尝试同样的事情:)所以在采访中的正确答案是,"它不能发生在我的**代码中,因为我从不使用`with`." (65认同)
  • @Pointy - 而且,我在严格模式下编程,其中不允许使用`with`. (7认同)
  • @Pointy在接受的答案中他们做了类似的事情而没有`with`所以它可能发生 (6认同)
  • @JonasW.很多人仍然使用`==`但是我没有看到`with`因为......实际上从来没有在JS文档之外,它说"请不要使用它".无论如何,一个很好的解决方案 (4认同)
  • @jorrit 没有人会使用 `==`。并且 `====` 阻止接受的答案 (2认同)
  • @Pointy没有`with`你可以做到:/sf/answers/3380171931/ (2认同)
  • 我绝对发现这是解决这个问题的最独特、最有创意和最不偷偷摸摸的方式(奇怪的间距)! (2认同)

geo*_*org 500

没有getter或valueOf的示例:

a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);
Run Code Online (Sandbox Code Playgroud)

这是因为==调用toString调用.joinArrays.

另一个解决方案,使用的Symbol.toPrimitive是ES6相当于toString/valueOf:

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

console.log(a == 1 && a == 2 && a == 3);
Run Code Online (Sandbox Code Playgroud)

  • 老实说,我认为这是最好的答案.它没有任何异常,只是设置了一些值.即使有基本的JS知识也很容易理解.做得好. (26认同)
  • 这非常有意义,几乎感觉很有用. (14认同)
  • 我真的很喜欢这个解决方案,因为除了对象拥有的连接函数之外,你不会覆盖任何东西,它只是一个非常干净且易于阅读的hack,使得逻辑评估为true. (11认同)
  • `没有valueOf`,嗯......它更间接,但基本上是一样的. (9认同)
  • 我知道大多数答案都是关于滥用`toString`或`valueOf`但是这个让我完全失控.非常聪明,我不知道它在内部调用`.join`,但它完全有道理. (7认同)
  • `a.join = a.shift` 是天才。邪恶的天才,但仍然是天才。 (2认同)

oco*_*mfd 264

如果询问是否可能(不是必须),它可以要求"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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 即使我知道其他解决方案,我也会故意给出这个答案,因为它回答了问题,但显然不是他们追求的问题.玩愚蠢的游戏,赢得愚蠢的奖品. (100认同)
  • @Piyin如果需要1000多次试验,你就赢了奖! (9认同)
  • 我喜欢这个答案,因为把它推到极致表明,如果在程序运行时cpu的寄存器/缓存被足够的宇宙射线击中,或者如果故意执行电源故障使得故障分支if条件实际上并没有跳跃. (4认同)
  • 但如果需要1000多次试验呢? (2认同)

Kos*_*Kos 208

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

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

if (a == 1 && a == 2 && a == 3) {
    console.log("!")
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理valueOf是当Object与原语(如Number)比较时调用的自定义方法.主要技巧是a.valueOf每次返回新值,因为它exec使用gflag 调用正则表达式,这导致lastIndex每次找到匹配时更新该正则表达式.所以第一次this.r.lastIndex == 0,匹配1和更新lastIndex:this.r.lastIndex == 1,所以下次正则表达式将匹配2,依此类推.

  • @Abdillah一个正则表达式对象将记住它匹配的最后一个索引,再次调用`exec`将从该索引开始搜索.[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#Description)不是很清楚. (22认同)

meh*_*mpt 190

如果a两个Web工作人员通过SharedArrayBuffer以及一些主脚本访问变量,则可以进行此操作.可能性很低,但是当代码编译成机器代码时,Web工作者可能及时更新变量a以满足条件a==1,a==2并且a==3满足.

这可以是Web工作者和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)
Run Code Online (Sandbox Code Playgroud)

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')
})
Run Code Online (Sandbox Code Playgroud)

modifier.js

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

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

在此输入图像描述

第二次尝试:

在此输入图像描述

正如我所说,机会很低,但如果有足够的时间,它就会达到最佳状态.

提示:如果您的系统需要太长时间.尝试a == 1 && a == 2Math.random()*3改为Math.random()*2.添加越来越多的列表会降低击中的可能性.

  • 老实说,这是最好的答案.所有其他答案都需要刻意尝试做一些非常不直观的事情.这个答案实际上反映了现实世界中可能发生的事情 - 竞争条件. (50认同)
  • 那么,你曾经在"汽车引擎控制器"和"箔条和火炬分配器系统"上工作过,这些系统是用网络工作者的javascript编程的吗?我不认为我会再去外面. (38认同)
  • 不仅如此 - 我实际上已经在现实世界中看到过这种情况.不是问题中的确切条件,但肯定是在函数的开头检查(a == 1)和函数后面的(a == 2),并且代码命中两个条件.仅供参考,我第一次看到这种情况发生在汽车发动机控制器中,我们制定了编码标准.第二次是用于军用飞机的箔条和火炬分配系统,在我*公司的第一天*我找到并修复了它,而团队的其他成员仍在讨论这个问题.(荣誉级别:高!:) (34认同)
  • @psaxton :)当然不是 - 但我们有多线程软件和共享数据.这是所有多线程软件的反模式,不是特定于Javascript或Web工作者.无论您是使用汇编语言,Brainf\*ck,Visual BASIC,C还是Javascript进行编程都无关紧要 - 如果您在多线程应用程序中使用共享数据执行此操作,它将始终***失败. (12认同)
  • 我认为现在这是围绕@ jontro答案的精心设计的包装器. (4认同)
  • @mehulmpt我不知道web worker可以改变主线程上的变量.你能提供一些示例代码吗? (2认同)
  • 呃,你如何通过SharedArrayBuffer访问*变量*?尽管多个工作人员可以共享缓冲区,但它们无法共享变量. (2认同)
  • 这个问题似乎只有3个答案:`toPrimitive` /`valueOf` /`toString`,`with`语句中的getter属性,或者全局对象的getter.其他一切只是主题"如何使函数在不同时间返回不同值"的变体. (2认同)

jon*_*tro 189

它可以在全局范围内使用以下内容完成.为了nodejs使用global,而不是window在下面的代码.

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

此答案通过定义用于检索变量的getter来滥用执行上下文中的全局作用域提供的隐式变量.

  • 问题是,这"永远"是否真实.答案是肯定的,这是可能出现的情况之一:`a`不是局部变量,而是在全局范围内使用递增的getter定义. (14认同)

Pat*_*ark 146

使用一系列自覆盖吸气剂也可以实现这一点:

(这类似于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.");
    }
})();
Run Code Online (Sandbox Code Playgroud)

  • 请注意,使用getter的方法也适用于`===`,而不仅仅是`==`. (60认同)
  • 请注意,这也适用于较少的缩进. (25认同)

Nin*_*olz 129

或者,您可以使用它的类和检查的实例.

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

var a = new A;

if (a == 1 && a == 2 && a == 3) {
    console.log('bingo!');
}
Run Code Online (Sandbox Code Playgroud)

编辑

使用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!');
}
Run Code Online (Sandbox Code Playgroud)

  • 只是`函数A(){value = 0;`一开始? (4认同)

Dra*_*18s 129

我没有看到这个答案已经发布,所以我也会把这个问题扔进去.这类似于Jeff对半宽韩文空间的回答.

var a = 1;
var ? = 2;
var ? = 3;
if(a == 1 && ? == 2 && ? == 3) {
    console.log("Why hello there!")
}
Run Code Online (Sandbox Code Playgroud)

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

a- 拉丁文小写字母A
?- 全宽拉丁文小写字母A
?- 西里尔文小写字母A.

这个通用术语是"同形文字":看起来相同的不同的unicode字符.通常很难得到三个完全无法区分的东西,但在某些情况下你可以获得幸运.A,Α,А和Ꭺ会更好地工作(拉丁语-A,希腊阿尔法,西里尔-A ,以及切诺基-A分别;不幸的是,希腊和切诺基小写字母是从拉丁文太不一样了a:?,?,所以没有按不帮助上面的代码片段.

那里有一整类的Homoglyph Attacks,最常见的是假域名(例如.wikipedi?.org(Cyrillic)vs wikipedia.org(Latin)),但它也可以出现在代码中; 通常被称为被贬低(如评论中提到的,[卑鄙]问题现在在PPCG上是偏离主题的,但过去常常会出现这种类型的挑战).我用这个网站找到了用于这个答案的同形词.

  • ["轻微的差异"](https://i.stack.imgur.com/NPo2j.png)不是我怎么称呼的. (19认同)
  • 您还可以使用unicode变体选择器(U + FE00..U + FE0F).这些都不是'a`:`a︀``a︁``a︂`.不再担心差异. (10认同)
  • @hvd完全取决于你的字体渲染.[这就是我所看到的](https://i.stack.imgur.com/6POUG.png). (4认同)

RAM*_*RAM 104

对的,这是可能的!

»JavaScript

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

if?(a==1 && a== 2 && a==3)
{
    document.write("<h1>Yes, it is possible!</h1>")
}
Run Code Online (Sandbox Code Playgroud)

上面的代码是一个简短的版本(感谢@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;}
Run Code Online (Sandbox Code Playgroud)

如果你只看到我的代码的顶部并运行它你说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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

现场演示

  • javascript版本是一种真正的反人类罪行,并且能够做到这一点,联合国公约应该是非法的.我认为现在是时候我们清除所有关于javacript的知识了. (55认同)
  • 为什么你在使用`document.write`?无论其余的答案如何,这都是一种不会被雇用的绝对方式. (4认同)
  • @Cerbrus,谢谢你的提示.我首先用`console.log`写了我的答案,但我把它改成了document.write.我总是在我的代码中使用`console.log`,但在这里我只想在StackOverflow代码片段框中向用户显示文本.所以我想让我的信息比`console.log`生成的信息更美.单击我的答案和其他答案的"运行代码片段"按钮.SO Code Snippet让我使用html和JS以及CSS然后我想在我的回答中使用它并使其变得更好.我认为它没有任何负面的副作用,并没有使我的答案大或收集. (3认同)
  • 功能声明甚至可以更短.`如果=()=>!0` (2认同)

Eri*_*nil 97

JavaScript的

a == a +1

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

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

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

确实,这不是采访者所要求的(它不起作用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
Run Code Online (Sandbox Code Playgroud)

或者增加a:

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

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end
Run Code Online (Sandbox Code Playgroud)

蟒蛇

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!")
Run Code Online (Sandbox Code Playgroud)

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.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @cᴏʟᴅsᴘᴇᴇᴅ:Java,Javascript,potayto,potahto :)已经有足够好的JS答案了.我只是觉得展示如何在其他语言中完成它可能会很有趣,并且可能会给JS开发人员一些想法. (27认同)
  • @cᴏʟᴅsᴘᴇᴇᴅ:用JS示例更新. (2认同)

Sal*_*n A 79

这是一个倒置的版本@杰夫的回答*其中隐藏字符(U + 115F,U + 1160或U + 3164)用于创建看起来像变量1,23.

var  a = 1;
var ?1 = a;
var ?2 = a;
var ?3 = a;
console.log( a ==?1 && a ==?2 && a ==?3 );
Run Code Online (Sandbox Code Playgroud)

*通过使用零宽度非连接器(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);
****/
Run Code Online (Sandbox Code Playgroud)

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


Mon*_*eus 74

规则第一的访谈; 从不说不可能.

不需要隐藏的角色欺骗.

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?' );
}
Run Code Online (Sandbox Code Playgroud)

  • 哎哟.`__ defineFetter__`实际上不是js语言的一部分,只是`defineProperty`的丑陋版本.`typeof`不是一个函数,这个未声明的`i`太可怕了.仍然似乎值得40 upvotes:/ (6认同)
  • @JonasW.41支票:-)我知道根据https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__不推荐使用`__defineGetter__`,但它在我的FireFox v中明确执行57.0.4所以我选择显示这个而不是`defineProperty()`,因为遗留代码是真实的,不能被忽略.无论丑陋如何,以我的方式宣称"我"是一个众所周知/记录在案的行为.也许我只是在一个PCG的心情¯\\ _(ツ)_ /¯ (6认同)

Fra*_*tti 68

但老实说,是否有办法让它评估为真或不(正如其他人所表明的那样,有多种方式),我要寻找的答案,作为进行了数百次访谈的人,将会是类似的东西:

"好吧,也许是在一些奇怪的情况下,对我来说并不是很明显......但如果我在实际代码中遇到这个问题,那么我会使用常见的调试技术来弄清楚它是如何以及为什么要做它正在做的事情然后立即重构代码以避免这种情况...但更重要的是:我绝对不会首先编写该代码,因为这是复杂代码的定义,我努力永远不会编写复杂的代码".

我猜一些采访者会冒犯一个显然意味着一个非常棘手的问题,但我不介意有意见的开发人员,特别是当他们能够用合理的思想支持它并且可以将我的问题与我的问题相吻合时关于自己的有意义的陈述.

  • 这不回答这个问题. (16认同)
  • 问题(或所有面试问题)可能是为了测试候选人是否愿意考虑问题,特别是那些"显而易见"的问题,就像这个问题一样.因为认为自己"知道"答案而拒绝思考的人不是一个好雇员. (13认同)
  • @JohnColeman这个问题询问代码如何评估为true.它首先没有询问面试官提出问题的原因.这个答案甚至没有试图解决被问到的问题,而是完全集中在一个"我会做什么"的试图猜测采访者的目的是什么的版本.如果这是问题,那就太宽泛了.因此,此答案不属于此处或网站上的任何位置. (8认同)
  • 关于这个答案令人遗憾的是,1rep用户昨天回答了这个问题并得到了2个downvotes,导致他删除了这个问题. (6认同)
  • @Don Hatch不,如果他们善意回答,我不会惩罚他们,特别是如果他们像其他人所说的那样给出了正确的答案......但我会要求后续人员尝试调查他们是否认为这是编写代码的好方法.知识渊博,能够提出"正确"的答案只是成为一名优秀开发人员的一部分.对于"专业"开发人员而言,更重要的是编写可以理解和维护的代码,通常由能力较弱的开发人员编写.过于聪明的开发人员与无能为力的IME一样糟糕. (5认同)
  • 我并没有从根本上不同意TylerH,因为我的回答没有直接解决所提出的问题.但与此同时,正如约翰科尔曼所说,可以说有一些价值.我认为,如果一个答案被认为是有价值的,即使是相切的话,它也会给SO社区带来一些好处,因此属于,最终投票是有价值的,什么有或不属于什么的仲裁者.我只是想解释为什么我不打算删除答案(没有人直接建议我应该,但这是隐含的). (2认同)
  • @Phil实际上"我仍然试图找到答案"意味着他们不仅仅想要'是或否'答案,而是如何解释.什么*不*暗示是"为什么". (2认同)
  • 换句话说,您永远不会维护自己没有编写的代码?在面试中以这种态度祝你好运。 (2认同)

Dir*_*mar 43

如果您遇到过这样的面试问题(或者在代码中注意到一些同样出乎意料的行为),请考虑哪些事情可能会导致乍一看似乎不可能的行为:

  1. 编码:在这种情况下,您正在查看的变量不是您认为的变量.这可能发生,如果你有意与使用Unicode勾搭同形字空格字符,使一个变量看起来像一个又一个的名字,但也可以不小心将编码的问题,例如,从包含意外的Unicode编码的网络复制和粘贴代码时点(例如,因为内容管理系统做了一些"自动格式化",例如用flUnicode'LATIN SMALL LIGATURE FL'(U + FB02)替换.

  2. 竞争条件:可能发生竞争条件,即代码未按开发人员预期的顺序执行的情况.竞争条件经常发生在多线程代码中,但多线程不是竞争条件可能的要求 - 异步性就足够了(不要混淆,异步并不意味着在引擎盖下使用多个线程).

    请注意,因为它是单线程的,因此JavaScript也不会没有竞争条件.请参阅此处获取简单的单线程 - 但异步 - 示例.在单个语句的上下文中,竞争条件在JavaScript中很难实现.

    与Web worker的JavaScript有点不同,因为您可以拥有多个线程.@mehulmpt向我们展示了使用网络工作者的一个很好的概念验证.

  3. 副作用:相等比较操作的副作用(不必像在这里的例子中那样明显,通常副作用非常微妙).

这些问题可以出现在许多编程语言中,而不仅仅是JavaScript,因此我们没有看到这里的经典JavaScript WTF之一1.

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

  • 副作用可能变得非常讨厌,并且精心设计的程序应该没有不必要的副作用.
  • 多线程和可变状态可能是有问题的.
  • 不进行字符编码和字符串处理可能会导致令人讨厌的错误.

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

  • @Edwin:原因完全相同:Unicode摆弄着看起来相似的字形或空格字符,竞争条件或比较操作的副作用(后者在我的例子中显示). (3认同)
  • 我希望这个答案可以用某种"元"方式标记到这个问题上.在阅读了上面的所有答案后,我感觉JS有*那么多*洞,但你只是一次性总结了所有的答案.你认为这样做的方式使得这成为一个出色的面试问题(如果删除了特定于语言的标签).好样的! (3认同)
  • @cᴏʟᴅsᴘᴇᴇᴅ:有时从更广泛的角度看问题有助于看到实际问题. (2认同)

Thé*_*ile 41

这是另一种变体,使用数组弹出你想要的任何值.

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

if(a == 1 && a == 2 && a == 3) {
  console.log('Yes');
}
Run Code Online (Sandbox Code Playgroud)


Bag*_*sIO 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!');
}
Run Code Online (Sandbox Code Playgroud)


Ice*_*You 28

使用代理:

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

代理基本上假装是目标对象(第一个参数),但拦截目标对象上的操作(在本例中为"get property"操作),这样就有机会做除默认对象行为之外的其他操作.在这种情况下,a==强制其类型时调用"get property"动作,以便将其与每个数字进行比较.有时候是这样的:

  1. 我们创建一个目标对象,{ i: 0 }其中i属性是我们的计数器
  2. 我们为目标对象创建一个代理并将其分配给 a
  3. 对于每次a ==比较,将as的类型强制转换为原始值
  4. 这种类型的强制导致a[Symbol.toPrimitive]()内部调用
  5. 代理拦截a[Symbol.toPrimitive]使用"获取处理程序"获取函数
  6. Proxy的"获取处理程序"检查获取的属性是什么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
Run Code Online (Sandbox Code Playgroud)

与大多数其他答案一样,这仅适用于松散的相等检查(==),因为严格的相等检查(===)不执行代理可以拦截的类型强制.

  • 尽管如此,在使用代理方面没有意义 - 在对象上以相同的方式定义`Symbol.toPrimitive`也可以正常工作. (2认同)

Gus*_*uez 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我不认为*每种*编程语言都有可能.例如,并非所有语言都有预处理器.就此而言,并非所有语言都使用`&&`作为逻辑"和". (27认同)
  • 你可以通过使用反射和搞乱整数缓存在Java中完成它. (7认同)
  • 不能用那些不支持突变的语言来做,例如haskell中没有可比的东西 (7认同)
  • 问题是询问JavaScript,而不是C++. (4认同)
  • 我发现了一种在[Python](https://trinket.io/python3/7fc8db9cf1)和[C++](https://codepad.remoteinterview.io/UZTDNYBOSN)中使用运算符重载的方法. (3认同)
  • 考虑到使用`std :: cout`,我非常怀疑它在C中有效 (3认同)
  • 这个问题不仅明确提到了javascript,而且还标记为javascript.C/C++完全无关紧要 (3认同)
  • @GustavoRodríguez*似乎其他人的答案得到了很多关于问题第一部分的赞成,包括Java中的例子,C#* - 是的,不要担心,我对所有这些都进行了投票.如果您认为"不相关"仅用于法庭,您可能想要查找该词的含义. (3认同)
  • @tas:我知道.你应该把它改成printf.但我想,你有了这个想法. (2认同)
  • 在ML中是不可能的.等式测试运算符(`op =`)是硬连线的,意思是平等,你不能将它重新定义为其他任何东西.此外,该语言区分值和包含值的可变单元格.您可以测试可变单元格是否相等,但是它会比较单元格的对象标识,而不是它们的内容. (2认同)

Pre*_*7or 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!');
}
Run Code Online (Sandbox Code Playgroud)

我的想法从数字对象类型方程的工作原理开始.

  • 也是第二次工作! (4认同)

Oma*_*ker 24

使用符号的ECMAScript 6答案:

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

由于==使用,JavaScript是应该强迫a到了接近第二个操作数(1,2,3在这种情况下).但是在JavaScript尝试自己进行强制攻击之前,它会尝试调用Symbol.toPrimitive.如果您提供Symbol.toPrimitiveJavaScript,则使用函数返回的值.如果没有,JavaScript会调用valueOf.


Gaa*_*far 24

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

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

if (a == 1 && a == 2 && a == 3) {
  console.log('Mind === Blown');
}
Run Code Online (Sandbox Code Playgroud)

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


Ben*_*bin 13

这个使用了defineProperty,它有一个很好的副作用导致全局变量!

var _a = 1

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

console.log(a)
console.log(a)
console.log(a)
Run Code Online (Sandbox Code Playgroud)

  • @NinaScholz肯定,但我们在这里谈论不好的做法 - 让我这样:D (13认同)
  • 你可以在`a`上使用一个闭包:`get:(a =>()=> ++ a)(0),`没有全局必要. (8认同)