Moderator note: Please resist the urge to edit the code or remove this notice. The pattern of whitespace may be part of the question and therefore should not be tampered with unnecessarily. If you are in the"whitespace is insignificant" camp, you should be able to accept the code as is.
有没有可能(a== 1 && a ==2 && a==3)可以在javascript中对true进行评估?
这是一家大型科技公司提出的面试问题。两周前就发生了,但我仍在努力寻找答案。我知道我们在日常工作中从来没有写过这样的代码,但我很好奇。
- 评论不用于扩展讨论;此对话已被移动到聊天。
- 请注意,即使更正了空格,原始字符仍可能使用不可见字符,并且似乎具有正确的间距。(a?== 1 && a == 2 &&?a == 3)(您只需在实际空格处进行替换)
- 对于那些显然投票将此掩饰得过于宽泛的人来说:这是在挖苦javascript,说有太多有效的答案吗?
- @ TrevorBoydSmith:除了JavaScript之外,这将在许多语言中使用,例如使用操作员重载C语言或Java中的副作用。
- 如果安全是一个人代码中的一个问题——而且应该是——那么询问细微差别和古玩(即使它们很模糊)并不一定是件坏事。如果面试官的问题不仅仅是理智上的独断专行,那么这是一种与候选人展开讨论的方式……所以@ghord,我不会把孩子和洗澡水一起扔出去,这是一个完全可以接受的工具;这是你在面试时用它做的事情,这是值得的。
- @如果面试官希望被面试者对ECMAScript有一些了解,那么Ghord是合理的。如果你知道==和===之间的区别,那么你可以很容易地回答这个问题。虽然韩国人的把戏很漂亮。
- 有些人坐在那里思考什么是可能的。其他人则把精力集中在是否为他们的客户开发可行的、正确的商业产品上。在我看来,这个问题没有实际的效用,除了这个事实之外,你不应该在面试中问这些问题或者写这些代码。这就是它应该关闭的原因。我的意思是,真的,企业意识到他们花钱请人坐下来谈论这些东西吗?
- javascript的线程模型是什么?在许多语言中(取决于a的定义,例如c中的volatile的定义),如果一个简单变量被外部的东西修改了,那么在不同的时间读取它可能会返回不同的结果。
- @tobyspeight javascript在单线程上下文中执行。某些操作确实在一个单独的线程上运行,但代码是同步运行的,因此在JavaScript中不会发生这种情况,至少在这个问题中不会有这样的表达式。
- 这个问题确实有一些优点,因为它需要理解你将要使用的语言的严重性和危险性。
- @开发人员被允许做愚蠢的事情并不意味着语言不好。阅读javascript的好部分。这是本书的重点。
- 读完答案后,故事的寓意是:当你指===时,不要使用==,有一个禁止非ascii变量名的编码标准,并且有一个执行前两种寓意的凌乱过程。
- @斯利克让我怀疑面试官是否会接受"我不知道,我只会像一个优秀的javascript开发者一样使用==="作为答案。(尽管就我个人而言,我确实与那种绝对主义有些争执,至少在小项目上是这样。)
- @Kryan当然有非常多的理由来解释为什么存在==和!=,这只是开发人员对这些(希望很少)用法置之不理的问题。
- 主持人注意:堆栈溢出有一段历史,人们用不同的语言来回答问题。这些都是试图回答这个问题,因为它们是解决一般问题的方法,尽管语言不同。请不要将其标记为"没有答案"。尽管如此,请不要用不同的语言发布更多的答案——正如这些其他答案下面的评论所指出的,这个问题是特定于javascript的原因,我们希望我们的特定于语言的问题保持不变。
- 值得一看:调用没有括号的函数
- @ DirkVollmar运算符重载在Java?
如果利用==的工作方式,您可以简单地创建一个具有自定义toString(或valueOf函数的对象,该函数可以在每次使用时更改返回的内容,从而满足所有三个条件。
1 2 3 4 5 6 7 8 9 10
| const a = {
i: 1,
toString: function () {
return a.i++;
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Hello World!');
} |
之所以这样做是因为使用了松散的相等运算符。使用松散相等时,如果其中一个操作数的类型不同于另一个操作数,则引擎将尝试将一个操作数转换为另一个操作数。如果对象在左边,数字在右边,它会试图通过首先调用valueOf将对象转换为数字,如果它是可调用的,如果它不能调用,它会调用toString。在这个例子中,我使用了toString,因为这是我想到的,valueOf会更有意义。如果我从toString返回一个字符串,那么引擎会尝试将该字符串转换为一个数字,给出相同的最终结果,尽管路径稍长一些。
- 你能通过改变隐含的valueOf()操作来实现这一点吗?
- 是的,出于同样的原因,工程价值取代了拖缆。
- 评论不用于扩展讨论;此对话已被移动到聊天。
- 这绝对是我最喜欢的答案之一。喜欢这个技巧,我从中学到了很多东西。嗯,我在想有什么方法可以让右边的接线员进去吗?那么任何给定的值都可能是真的。
- 幸运的是,没有。
- 如果i和1、2和3是数字,为什么叫toString?
- 根据这一点,将首先尝试数字转换,因此valueOf稍微好一些。
- @pureferret左边的相等比较是一个对象,而不是一个数字。该对象在i上有一个数字属性不会影响引擎。;)
- 只是好奇,这样的表达式a == 1 && a == 2 && a == 3不是要进行JIT优化吗?
- @Rkosegi井在这种情况下似乎没有得到优化。如果编译它,它可能会将表达式优化为false
- 为什么不使用this?即:toString: function() { return this.i++; }。
- @Rkosegi不,在JS中不可能。代码的行为与规范中定义的一样,即使经过优化
- 只是好奇,我们能支持a==1&;a==2&;a==3吗?例如,尝试重写number.constructor并使用装箱/取消装箱?
- @zxxc no、numbers和Numbers是不同的(一个是原始的,一个是对象,因此它们会立即失败严格的===比较)。数字原语没有构造函数(任何原语也没有)。
- @zxxc不适合这个答案,但下面三个
- 只是一个附加条件:const a = { i: 0, toString: function () { var returned = (++a.i); if(a.i >= 3) { a.i = 0; } return returned; } }。
- 评价顺序总是从左到右吗?询问是因为根据JS引擎,我们应该将i++语句编写为i--?
- 当与数字比较时,a的值是多少?
- 当它最终与数字进行比较时,它就是要与之比较的数字。但是,a包含一个对象。
我无法抗拒-其他答案毫无疑问是正确的,但你真的无法通过以下代码:
1 2 3 4 5 6
| 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。为了可读性,用_替换空格,相同的代码如下:
1 2 3 4 5 6
| var a_ = 1;
var a = 2;
var _a = 3;
if(a_==1 && a== 2 &&_a==3) {
console.log("Why hello there!")
} |
查看对Mathias变量名验证器的验证。如果这个奇怪的间隔真的包含在他们的问题中,我相信这是对这种答案的提示。
不要这样做。说真的。
编辑:我注意到(尽管不允许启动变量)在变量名中也允许使用零宽度连接符和零宽度非连接符字符-参见模糊化带零宽度字符的javascript-优缺点?.
如下所示:
1 2 3 4 5 6
| 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!")
} |
- 从原始问题的奇怪间距来看,我认为这正是面试问题所寻找的答案——利用看似空格的非空格字符。好地点!
- @Baracus是Ronjohn在评论Kevin的答案时注意到了奇怪的间隔,这让我想起了这个(糟糕的)技巧,所以我不能相信他能找到答案。不过,我有点惊讶,没人回答过这个问题,因为几年前我的工作因为某个地方有一篇博文——我有点以为到目前为止这是相当普遍的知识。
- 你能用U+2060或类似的代替吗?
- 当然,这是被禁止的标准漏洞,这也适用于采访。[需要引文]
- 呵呵,我可以想象,如果使用严格的平等,这会更像是一个思维失常者,然而,如果这是面试官想要的结果,那么答案可能会更明显。
- 考虑到原来的间距,可能会更糟,即使用了一个变量var ?2 = 3;所以有三个变量a??= 1, ?2 = 3, a = 3(a? = 1, ?2 = 3, a = 3),所以(a?==1 && a==?2 && a==3))…
- @埃德蒙德雷德:我可以想象一个错误的角色进入源代码,然后每个人都有一个魔鬼般的工作试图找到这个bug。(尤其是如果有人在韩国编码。)
- 有人能指出这三个变量之间的区别吗?我很难理解答案,即使在阅读了解释之后。我最初的想法是,所有三个变量在我看来都是一样的,我认为它们会互相覆盖,只有最后一个变量会占上风。那么,它们的行为又是如何不同的呢?
- 正确配置的静态代码分析工具应该捕获这样的错误代码,并在生成时报告它。储存库中不允许有这样的代码。
- @al-zami在两个变量中有一个额外的字符,在屏幕上显示为一个空格,但被解释为标识符的一部分,这意味着有三个单独的变量-a、a和a-额外的字符是朝鲜文的半宽空格。
- @霍格尔哈!可能只有一个变量"a",1、2和3都可以定义为与a值相同的变量。好时光…
- 嗯,在EDCOX1的18位或EDCOX1的19位之前没有可见的空间,但是我不排除有一个零宽度字符是一个有效的标识符开始…我不知道,JavaScript是如何处理这个的,但是Java允许使用LTR/RTL控制字符的一些乐趣…
- @霍尔格刚刚做了一些实验——看起来LTR字符至少在标识符的开头或结尾都不起作用。作为参考,这里是:"?"
- @Edmundreed只使用字母A可能没有那么明显,但是在比实习生更老的遗留代码中得到足够多的名称不好的表、字段、变量和缓冲区,没有什么会让你吃惊的。
- 虽然最高投票的答案显然是"正确的",但这一个是狡猾的,恶心的,而且…对了:埃德蒙德雷德,我认为你错了。杰夫的分析正是面试官想要的。有时,你会得到一段代码,由于一些不太明显的原因而失败,它需要一个在框外思考良好的人来发现它。
- @埃德蒙德赖德拥有这种奇怪/无用的知识,可能是一个很好的预测器,可以预测候选人是否还有其他确实有用的知识。
- @这是一个公平的观点,但是排除任何不知道这一点的候选人是愚蠢的。
- 这正是正确的答案,老实说,所有其他人都在给出同样的情况!
- @埃德蒙认为你是对的,不知道答案不应该是立即取消资格。但是,可能是开放的可能性,有一个答案是什么寻找?我认为我们可以安全地假设,这一个问题并不是整个面试的全部。
- @霍尔格,我刚刚发现零宽度的连接符和非连接符在变量名中起作用(尽管不是起始字符),参见编辑。好时光…
- 我发现这个美丽而恐怖的同时。做得好。
- JS应该已经和自己的编辑器一起来防止这种疯狂。
这是可能的!
1 2 3 4 5 6 7 8 9 10
| var i = 0;
with({
get a() {
return ++i;
}
}) {
if (a == 1 && a == 2 && a == 3)
console.log("wohoo");
} |
这将使用with语句中的getter,让a评估为三个不同的值。
…这仍然不意味着这应该在真正的代码中使用…
更糟糕的是,这种技巧也适用于===的使用。
1 2 3 4 5 6 7 8 9 10
| var i = 0;
with({
get a() {
return ++i;
}
}) {
if (a !== a)
console.log("yep, this is printed.");
} |
- 是的,我也在尝试同样的事情:)所以在采访中正确的回答是,"这在我的代码中是不可能发生的,因为我从来没有使用过with"。
- 尖尖的YUP。with无疑是一个设计错误。
- @尖头-而且,我在严格的模式下编程,其中不允许使用with。
- @在接受的回答中,他们做了一些类似的事情,没有with,这样就可以发生。
- @jorrit没有人会使用==。而===则阻止了被接受的答案。
- @ JonasW。很多人仍然使用==,但自从……实际上,千万不要超出JS文档中所说的"请不要使用它"。总之,一个很好的解决方案。
- @如果只有所示的部分存在,那么A就没有定义,问题也没有任何意义。但对于任何普通人来说,这个问题都是有意义的,所以每个普通人都会假设周围有一些简单的没有出现的附加代码。
- @很重要,你可以不使用with:stackoverflow.com/a/48288170/65387
- 注意,有了这个解决方案,即使使用===,条件也将被评估为真!
- 我绝对觉得这是解决这个问题最独特、最有创意、最不偷偷摸摸的方法(奇怪的间隔)!
- 我在答案中添加了三等号的例子。(是的,你可以不使用with来引诱==支票)
- MDN禁止在严格模式下使用来自ES5的with。我同意他们的理由。点击这里developer.mozilla.org/en-us/docs/web/javascript/reference/…
不带getter或value的示例:
1 2 3
| a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3); |
这是因为==调用toString,后者调用.join作为数组。
另一种解决方案是使用Symbol.toPrimitive,它是ES6相当于toString/valueOf:
1 2 3 4
| let i = 0;
let a = { [Symbol.toPrimitive]: () => ++i };
console.log(a == 1 && a == 2 && a == 3); |
- without valueOf嗯…它更间接,但基本上是一样的。
- 我真的很喜欢这个解决方案,因为除了对象本身的连接函数之外,您不需要重写任何东西,而且它只是一个非常干净且易于阅读的黑客程序,它使逻辑计算成为真实的。
- 老实说,我认为这是最好的答案。它不涉及任何异常的事情,只是设置了一些值。即使具备基本的JS知识,也很容易理解。做得好。
- 这很有意义,几乎觉得很有用。
- 我知道大多数答案都是关于虐待toString或valueOf,但这一次我完全措手不及。非常聪明,我不知道它在内部称为.join,但它完全有意义。
- a.join = a.shift是天才。邪恶的天才,但仍然是天才。
如果询问是否可能(不一定),可以要求"A"返回随机数。如果按顺序生成1、2和3,则为真。
1 2 3 4 5 6 7 8 9 10 11 12
| 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;
}
}
} |
- 我会故意给出这个答案,即使我知道其他的解决方案,因为它回答了这个问题,但显然不是他们想要的。玩愚蠢的游戏,赢得愚蠢的奖品。
- 但如果它需要1000多次试验呢?
- @如果它需要超过1000次的考验,你会赢得一个奖品!
- 我喜欢这个答案,因为把它推向极端表明,如果在程序运行时CPU的寄存器/缓存受到足够的宇宙射线的撞击,或者如果有人故意执行一个电源故障,这样的话,if条件的故障分支实际上就不会跳变,这在任何语言中都是可能的。
- @我看到这个问题,立刻想到了罗汉默。
- 最低:1,最高:412。
如果没有正则表达式无法执行任何操作:
1 2 3 4 5 6 7 8 9 10
| var a = {
r: /\d/g,
valueOf: function(){
return this.r.exec(123)[0]
}
}
if (a == 1 && a == 2 && a == 3) {
console.log("!")
} |
它的工作原因是自定义valueOf方法,当对象与原语(如数字)比较时调用该方法。主要的诀窍是,a.valueOf每次都返回新的值,因为它在带有g标志的正则表达式上调用exec,每次找到匹配时都会更新该正则表达式的lastIndex。所以第一次this.r.lastIndex == 0时,它匹配1并更新lastIndex:this.r.lastIndex == 1,所以下次regex将匹配2等。
- 嗯…这是怎么工作的?
- @一个regex对象将记住它匹配的最后一个索引,再次调用exec将从该索引开始搜索。MDN不是很清楚。
- 我明白了,所以this.rregex对象记住状态/索引。谢谢!
- !
- 不过,我建议将一个字符串传递给exec,而不是一个要字符串化的整数。
- 使用regex,现在有两个问题
- 我会记住这个方法,以防我需要完全混淆我的代码!
它可以在全局范围内使用以下方法完成。对于nodejs,在下面的代码中使用global,而不是window。
1 2 3 4 5 6 7 8 9
| var val = 0;
Object.defineProperty(window, 'a', {
get: function() {
return ++val;
}
});
if (a == 1 && a == 2 && a == 3) {
console.log('yay');
} |
这个答案通过定义getter来检索变量,从而滥用了执行上下文中全局范围提供的隐式变量。
- 这假设a是this的财产,它似乎不是。如果a是一个局部变量(它看起来是这样的),那么这将不起作用。
- @你的意思是如果你把var a放在某个地方?
- 是啊。引用a == 1意味着a是某个变量,而不是this的属性。虽然有一个像globals这样的奇怪地方,两者都可能是真的,但一般来说,用var a或let a声明变量意味着没有this允许您访问a作为代码假定的属性。所以,您的代码显然假设了一些奇怪的全局变量。例如,您的代码不能在node.js中工作,也不能在函数内部以严格模式工作。您应该详细说明它的具体工作环境,并可能解释它的工作原因。否则,这是误导。
- @朋友00当然。不确定它是否会与其他已经回答过的问题结合起来增加更多的价值。将更新答案
- 问题是,这"永远"是真的吗?答案是肯定的,这可能是正确的情况之一:a不是局部变量,它是在全局范围内用递增getter定义的。
- 您可以通过执行this.val = this.val || 0;并使用this.val来保存全局变量val。这样它就可以自给自足了。
这在变量a被访问的情况下是可能的,比如说2个Web工作者通过sharedraybuffer和一些主脚本进行访问。可能性很低,但当代码编译为机器代码时,Web工作者可能会及时更新变量a,从而满足a==1、a==2和a==3的条件。
这可以是Web工作者和JavaScript中的SharedarrayBuffer提供的多线程环境中的争用条件示例。
下面是上述的基本实现:
MIN JS
1 2 3 4 5 6 7 8
| // 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) |
作业工人
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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')
}) |
JS
1 2 3 4 5
| addEventListener('message' , ({data}) => {
setInterval( () => {
new Uint8Array(data)[0] = Math.floor(Math.random()*3) + 1
})
}) |
在我的MacBook Air上,第一次尝试大约100亿次迭代后会发生这种情况:
第二次尝试:
如我所说,机会很小,但只要有足够的时间,它就会达到状态。
提示:如果您的系统需要太长时间。只尝试a == 1 && a == 2,将Math.random()*3改为Math.random()*2。越来越多地添加到列表中会降低命中率。
- 说实话,这是最好的答案。所有其他的答案都需要有意识的尝试去做一些完全没有意义的事情。这个答案实际上反映了现实世界中可能发生的事情——种族状况。
- 不仅如此,我还亲眼目睹了这一切发生在现实世界中。不是使用问题中的确切条件,而是在函数开始时检查(a==1),在函数之后检查(a==2),并让代码同时满足这两个条件。仅供参考,我第一次看到这种情况发生是在一个汽车发动机控制器,我们把编码标准到位。第二次是在军用飞机的颖壳和火炬分配器系统中,在我在公司的第一天,我发现并修复了这个问题,而团队的其他成员仍在讨论这个问题。(荣誉等级:高!:)
- 那么,你已经研究过"汽车引擎控制器"和"颖壳和火炬分配器系统",它们是用JavaScript和网络工作者编程的?我想我不会再出去了。
- @当然不是,但是我们有多线程的共享数据软件。这是针对所有多线程软件的反模式,而不是针对javascript或Web工作者。不管您是用汇编语言、Brainf*ck、VisualBasic、C还是JavaScript编程——如果您在多线程应用程序中使用共享数据进行编程,它总是会失败。
- @Mehulmpt我不知道Web工作者可能会在主线程上改变变量。您能提供一些示例代码吗?
- @QNTM变量不在主线程中。一切都在访问共享内存
- 呃,如何通过sharedraybuffer访问变量?即使多个工作人员可以共享缓冲区,他们也不能共享变量。
- 这是不可能的(这也是sharedraybuffer存在的原因)。
- @Bergi检查上面的示例
- @qntm检查上面的示例
- @在您当前的示例中,您测试的是a[0] == 1 && a[0] == 2 && a[0] == 3,而不是a == 1 && a == 2 && a == 3。这不是最初的问题所要求的。你能用a而不是a[0]展示一个例子吗?
- @QNTM此更新应该让您高兴:)
- 我认为这是一个围绕@jontro答案的精心包装。
- 当然。只需要包装器从名为"a"的变量访问共享内存。它是共享数组缓冲区,:)和数字,我们现在正在研究,它们不是由引用保存的,而是由值保存的。这样做,a=array[0]就是把array[0]的当前值复制到a上,这是无意的行为。希望对@qntm有所帮助
- 这个问题似乎只有3个答案:toPrimitive/valueOf/toString,with声明中的getter属性,或全局对象的getter。其他一切都只是主题"如何使函数在不同的时间返回不同的值"的变体。
- 在没有sharedraybuffer的情况下,有什么方法可以使这个工作正常吗?"请注意,SharedArrayBuffer在1月5日的所有主要浏览器中都被默认禁用了,2018是针对Simele的。"我的假设是,如果您继续循环共享变量(1,2,3,1,2,3,1,2,3,1,…),那么您将获得成功,而不是将其分配为随机数,就像我在一些相关的多线程Java应答中所做的那样。
- @erwinbolwidt sharedraybuffer返回
这也可以使用一系列自重写getter:
(这类似于jontro的解决方案,但不需要计数器变量。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| (() => {
"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.");
}
})(); |
- 注意,使用getter的方法也适用于===,而不仅仅是==。
- 请注意,这也适用于较小的缩进。
- 这个解决方案依赖于this作为arrow函数主体内的全局对象。
- @帕特里克罗伯茨和这里的一切不是吗?
- @午夜时分,我不会把其他答案归为"金字塔密码"。
- 注意这也适用于任意顺序,不是吗?比如,(a == 3 && a == 2 && a == 1)?
我看不到这个答案已经发布了,所以我也会把这个加入到混合中。这类似于Jeff的半宽朝鲜文答案。
1 2 3 4 5 6
| var a = 1;
var a = 2;
var а = 3;
if(a == 1 && a == 2 && а == 3) {
console.log("Why hello there!")
} |
你可能会注意到与第二个存在细微的差异,但第一个和第三个与肉眼相同。所有3个字符都是不同的字符:
a—拉丁文小写Aa—全角拉丁文小写Aа—西里尔文小写A
它的通用术语是"同形符号":不同的Unicode字符看起来相同。通常很难得到三个完全不可区分的,但在某些情况下你可以幸运。A、_、А和?效果会更好(拉丁字母a、希腊字母a、西里尔字母a和切罗基字母a;不幸的是,希腊字母和切罗基小写字母与拉丁字母a太不一样:α、?,因此对上述片段没有帮助。
现在有一类完全相同的攻击,最常见的是假域名(如EDOCX1(西里尔文)vs EDOCX1(拉丁语)),但它也可以用代码显示;通常被称为欠手(如评论中提到的,[欠手]问题现在在ppcg上不再是话题,但过去是一种挑战,其中会出现类似的情况)。我用这个网站找到了这个答案的同形词。
- "细微的差异"不是我怎么称呼它的。
- @hvd完全取决于字体渲染。这就是我看到的。
- 如果你想要第三个选择。
- @杰克,是的,全宽的拉丁文小写字母A不是最伟大的同形符号(但大写字母的变体是惊人的)。一般来说,你只需要两个就可以得到想要的效果。
- @Draco18同意回复:通常只需要2个。额外信息也很好!
- 您还可以使用Unicode变量选择器(U+FE00..U+FE0F)。这些都不是a:a?a?a?。不再担心差异。
- 哦,不。你打破了单太空!
或者,您可以使用一个类和一个实例来进行检查。
1 2 3 4 5 6 7 8 9 10
| 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类,看起来像这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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!');
} |
- 刚开始的时候是不是只有function A() {value = 0;?
- valueOf被重写,this method is usually called automatically by JavaScript behind the scenes, and not explicitly in code所以当我们比较它实际增加的值时。
是的,这是可能的!????JavaScript
1 2 3 4 5 6 7
| if?=()=>!0;
var a = 9;
if?(a==1 && a== 2 && a==3)
{
document.write("Yes, it is possible!??")
} |
以上代码是一个简短的版本(感谢@forivin在注释中的注释),以下代码是原始代码:
1 2 3 4 5 6 7 8 9 10 11
| var a = 9;
if?(a==1 && a== 2 && a==3)
{
//console.log("Yes, it is possible!??")
document.write("Yes, it is possible!??")
}
//--------------------------------------------
function if?(){return true;} |
If you just see top side of my code and run it you say WOW, how?
So I think it is enough to say Yes, it is possible to someone that said to
you: Nothing is impossible
Trick: I used a hidden character after if to make a function that its name is similar to if. In JavaScript we can not override keywords so I forced to use this way. It is a fake if, but it works for you in this case!
?C.*
我还写了一个C版本(增加属性值技术):
1 2 3 4 5 6 7 8 9 10
| 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!??");
}
} |
现场演示
- function if<200c> { }如何工作?为什么不与function if<123c>{}一起工作?
- @muthukumar,我在回答中使用了EDOCX1(零宽度非连接符)字符,你想要if和\u123c一起使用?好,写if,然后按Alt,按+,按123c,释放Alt,使这个字&187;if?。
- 狡猾的一个,@kevin b也给了优秀的ans
- @是的,凯文布的回答很好。我的JS答案有技巧,我也用C版本更新了我的答案(它没有技巧);
- javascript版本是一种真正的反人类犯罪,并且这样做的能力,应该是联合国公约所禁止的。我认为是时候我们净化JavaRipt的所有知识了。
- 函数声明可能更短。if?=()=>!0
- 你到底为什么使用document.write?不管剩下的答案如何,这绝对是不被雇佣的好方法。
- @塞尔布鲁斯,谢谢你的留言。我先用console.log写了我的答案,但我改为document.write。实际上,我总是在代码中使用console.log,但这里我只想在stackoverflow代码段框中向用户显示文本。所以我想展示我的信息比console.log生成的信息更漂亮。单击我的答案和其他答案上的Run Code Snippet按钮。So代码片段让我可以使用HTML、JS和CSS,然后我想在我的答案中使用它,并使其更漂亮。我认为它没有任何负面的副作用,也没有使我的回答大或完整。
- @更清楚的是,如果联合国公约能够有效地改变世界,那么我们应该有一个比这更好的世界。我们需要的不仅仅是联合国的声明,直到那天我认为我们可以使用我的javascript技巧;)
- @拉姆,你是说联合国和世界基本上没有关系吗?亵渎!
- @更清楚的是,我说我们的世界是黑暗的,其原因之一是,我们世界上最重要的组织之一联合国并没有如预期的那样有效。亵渎是当我们坚持盲目,而我们可以勇敢的心和开放的眼睛。
- 你所说的"财产增值技术"是什么意思?
- @彼得莫滕森谢谢你的问题,这是我的选择。我指的是我在C版中使用的技术(每次我们得到它之后,增加一个单位到属性的值)。本页中的许多答案,如已接受的答案都是这样使用的,我在答案中将其命名为"增加属性技术"。
- @Petermortensen:也许ram不是英语母语者,意思是"技术",也就是说,每次取a的值时,都要增加一个属性的技术。
- 虽然由于不正当行为而获得满分,但这并不能回答问题,因为不使用(a== 1 && a ==2 && a==3)的值。
JavaScriptA==A+ 1
在javascript中,没有整数,只有Numbers,它们被实现为双精度浮点数。
这意味着,如果一个数字a足够大,它可以被视为等于三个连续整数:
1 2 3 4
| a = 100000000000000000
if (a == a+1 && a == a+2 && a == a+3){
console.log("Precision loss!");
} |
的确,这不是面试官所要求的(它不适用于a=0),但它不涉及隐藏函数或运算符过载的任何技巧。
其他语言
作为参考,Ruby和Python中有a==1 && a==2 && a==3解决方案。稍加修改,Java也是可能的。
红宝石
有了自定义==:
1 2 3 4 5 6 7 8 9 10 11
| class A
def ==(o)
true
end
end
a = A.new
if a == 1 && a == 2 && a == 3
puts"Don't do this!"
end |
或增加的a:
1 2 3 4 5 6 7 8
| def a
@a ||= 0
@a += 1
end
if a == 1 && a == 2 && a == 3
puts"Don't do this!"
end |
Python
1 2 3 4 5 6 7
| 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 EDCOX1×14缓存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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.");
}
}
} |
- 编码高尔夫SE有人吗?
- 问题被标记为JS?
- @ C????S????JAVA、JavaScript、PoTayto、PotoHTO:已经有足够好的JS答案了。我只是觉得展示如何用其他语言来完成它会很有趣,并且可能会给JS开发人员一些想法。
- @ C????S?????:用JS示例更新。
- 为什么Java版本不使用EDCOX1×0(或它)来工作?据我所知,自动氧化,Integer a = 42; a == 1 && a == 2 && a == 3应该把所有的ints装箱。还是取消对a的装箱以便进行比较?
- @CAD97:Integer == int似乎导致拆箱。但是使用Integer#equals(int)会强制自动氧化,所以它是有效的。谢谢你的评论!
- 在javascript中肯定有整数…
- @史蒂芬:请解释一下。据我所知,JS中只有Numbers,基本上类似double,它们可以看起来像整数,可以像整数一样使用,但它们仍然不是整数。我认为在Java/Python/C/Ruby/整数中,EDOCX1ω6可能不是真的。
- @斯蒂芬·比基特:那么……你的论点是什么?
- 没什么好争论的。他们要么在那里,要么不在,他们在那里。
- @史蒂芬比吉特:"他们要么在那里,要么不在。"可以。所以JS中绝对没有整数。1.0可以显示为1,但它仍然是一个浮点型。
- 您使用最大值,但正确的答案是使用对象原型函数声明。
- @根据规范要求,所有数字都要像双精度浮点小数一样。但是,在不存在溢出威胁的情况下,许多浏览器优化并在内部将数字存储为整数是正确的。
- 不回答问题,但这是一个真正迷人的答案。谢谢您!
- 有趣的方法!!!!
这是@jeff's answer*的倒装版本,其中使用隐藏字符(u+115f、u+1160或u+3164)创建类似于1、2和3的变量。
1 2 3 4 5
| var a = 1;
var ?1 = a;
var ?2 = a;
var ?3 = a;
console.log( a ==?1 && a ==?2 && a ==?3 ); |
*这个答案可以通过使用零宽度非连接符(U+200C)和零宽度连接符(U+200D)来简化。这两个字符都允许出现在标识符内,但不能出现在开头:
1 2 3 4 5 6 7 8 9 10 11
| 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)。
- 也是我答案的一个变种,所以有一个+1。我也收到了一些无法解释的否决票。
面试的第一条规则;绝不说不可能。
不需要隐藏人物的诡计。
1 2 3 4 5 6 7 8 9 10 11
| 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?' );
} |
- 哎哟。__defineGetter__实际上不是JS语言的一部分,只是defineProperty的一个丑陋版本。typeof不是一个函数,这个未声明的i很糟糕。似乎仍然值得40票赞成:/
- @ JonasW。41票赞成:—)我知道__defineGetter__对于每个开发者都是不赞成的。mozilla.org/en-us/docs/web/javascript/reference/&hellip;但是它在我的firefox v 57.0.4中执行得很清楚,所以我选择显示它而不是defineProperty(),因为遗留代码是真实的,不能被忽略。不管多么丑陋,用我的方式来宣布i是一种众所周知的/有据可查的行为。也许我只是在一个PCG情绪&175;()&175;
老实说,不管有没有一种方法可以让它评价为真(正如其他人所展示的,有多种方法),我要寻找的答案是,作为一个已经进行了数百次采访的人来说,应该是这样的:
"嗯,也许是的,在一些我不太清楚的奇怪情况下……但是,如果我在真实代码中遇到这种情况,那么我将使用常见的调试技术来弄清楚它是如何做的以及为什么要做它正在做的事情,然后立即重构代码以避免这种情况……但更重要的是:我绝对不会首先编写代码,因为这正是复杂代码的定义,我努力避免编写复杂代码"。
我想有些面试官会对一个显然是非常棘手的问题而生气,但我不介意有意见的开发人员,特别是当他们能够用合理的想法来支持它,并能将我的问题融入到一个有意义的关于他们自己的陈述中时。
- So,would you penalize some one who takes your question in good faith,and is so knowledgable that they can actually answer it correctly?I'd be pretty upset if someone d i d that to me.Not sure whether that's your intent,but if it's not,you might want to edit your answer to make it clear.
- The question(or all interview questions)is probably to test the candidates willingness to think about a problem,especially ones that are"apparently obvious",like this one.有些人因为相信他们而拒绝思考,他们"知道"答案不是一个好主意。
- @Don Hatch no,I would uldn't penalize them if they answered in good faith and especially if they gave a correct answer like those others have shown…但是,我会要求他们采取后续行动,尝试和尝试,他们认为这是一个好的方式写代码或不。Being knowledgeable and being able to come up with a"correct"answer is only part of being a good developer.Far more important for a"professional"developer is writing code that is understandable and maintainable down the road,Often Times by less capable developers.主要的Clever developers are pretty much as bad as incapable ones ime.
- This doesn't answer the question.
- The sad thing about this answer is that a 1rep used answered that yesterday and got 2 downvotes causing him to delete this question.
- @Tyler since this gives an intelligent way to answer the interview question and op was asking about how they should have answered that question,I think that this is a perfectly valid answer.It probably should n't be the accepted answer,but it does Add some useful(hence the many upvotes,including mine).
- @Johnkleman the question asks the code could evaluate to true.It doesn't ask the reasons the interviewer proposed the question in the first place.This answer does't even attempt to address the question being asked,and instead focuses entirely on a"What I would do"version of an attempt a t guessing what the interviewer's purpose was.如果这是一个问题,它将是一个非常广泛的方式。There fore this answer doesn't belong here or anywhere on the site.
- I don't fundamentally disagree with tyler in that my answer does not directly address the question asked.但在同一时间,作为约翰·科尔曼说,有一些价值。I would argue that if an answer is deemed meritorious,even if tangential,then i t offers some benefit to the so community and thus belongs,and finaltately votes are the arbiter of what has merit and what does or doesn't belong.I just wanted to explain why I'm not going to delete the answer(no one directly suggested I should,but it's implied).
- @Tyler the question asks if the code could evaluate to true,not how.
- @Phil actually"I'm still trying to find the answer"implies pretty heavely that they don't just want a'yes or no'answer,but an explanation of how.What's not implied is"Why".
- 在其他的话,你从来没有保持代码你没有写你自己?Good luck with that attitude in interviews.
- Shammoo,now tell me honestly,how many times you'd use any of those answers in production code?Whoever does think about this except for amusement is"not a good hire".
- @volkov我使用思想来生成我的大部分(希望)生产代码。每当我看到我自己或任何人在没有进一步思考的情况下使用第一个想法时,解决方案通常很差,需要很长的时间来构建。
这是另一个变体,使用数组弹出您想要的任何值。
1 2 3 4 5 6 7 8 9 10
| const a = {
n: [3,2,1],
toString: function () {
return a.n.pop();
}
}
if(a == 1 && a == 2 && a == 3) {
console.log('Yes');
} |
如果你遇到这样一个面试问题(或者注意到你的代码中有一些同样意想不到的行为),想想什么样的事情可能导致乍一看是不可能的行为:
编码:在这种情况下,您看到的变量不是您认为的那个变量。如果您有意使用同形符号或空格字符来混淆Unicode,使变量名称看起来像另一个变量,则可能会发生这种情况,但也可能会意外引入编码问题,例如,从包含意外Unicode代码点的Web复制和粘贴代码时(例如,由于内容管理系统Did一些"自动格式化",例如用unicode"拉丁文小型连字fl"(u+fb02)替换fl。
竞态条件:可能会发生竞态条件,即代码没有按照开发人员预期的顺序执行的情况。多线程代码中经常会出现争用条件,但多个线程不是争用条件可能实现的要求——异步性足够(不要混淆,异步并不意味着多个线程是在后台使用的)。
请注意,因为JavaScript是单线程的,所以它也不是没有竞争条件的。请参阅这里的一个简单的单线程但异步的例子。然而,在单个语句的上下文中,竞态条件在JavaScript中很难达到。
Web工作者的JavaScript有点不同,因为您可以有多个线程。@Mehulmpt已经向我们展示了使用Web工作者的一个很好的概念证明。
副作用:相等比较操作的副作用(不必像这里的例子那样明显,通常副作用非常微妙)。
这些问题可以出现在许多编程语言中,不仅仅是JavaScript,因此我们在这里没有看到经典的JavaScript WTF。
当然,这里的面试问题和样品看起来都很做作。但它们提醒我们:
- 副作用会变得非常严重,一个设计良好的程序应该没有不必要的副作用。
- 多线程和可变状态可能存在问题。
- 不正确地进行字符编码和字符串处理会导致严重的错误。
1例如,您可以找到一个完全不同的编程语言(c)的示例,其中显示了一个副作用(明显的一个)。
- 然后,问题变得太广泛了。不同的语言可以不同程度地轻松实现这一点。这个问题得到了如此多的关注,因为它是一个JS特定的问答,但那只是我的2c。
- 原因是不同的c_和javascript,所以这个答案是不合法的。
- @edwin:原因完全相同:Unicode处理相似的外观标志符号或空格字符、竞争条件或比较操作的副作用(在我的示例中显示的是后者)。
- @ C????S?????:有时从更广的角度看问题有助于发现实际问题。
- 我的意思是,你最初的答案(现在是代码部分)并不能完全解决这个问题。但是,通过重新编辑您的答案,我同意您的观点,问题的其他方面可能会在JS或其他语言中出现。
- 我看不出这三个点中有一个真正匹配。javascript中没有竞争条件。编码没什么区别。副作用…如果考虑到这个事实,每个对变量的访问都会从堆栈中查询它。对。但我仍然认为这是故意让它像那样运作的。
- @Gottz:请注意,竞态条件不需要多个线程,编码会产生差异,并且副作用在JS中是可能的。面试问题需要有意识地编写代码,但是你应该把它作为一个例子来证明如果开发者不注意这些细节是可能的。
- 我希望这个答案能以某种"元"的方式标记到这个问题上。读完上面所有的答案后,我觉得JS有那么多漏洞,但你只需一次就把所有的答案总结出来。你这样做的方式,使这成为一个明星采访问题(如果语言特定的标签被删除)在我看来。好极了!
- 异步性不是在引擎盖下使用不同的线程,还是这是一种误解?
- @不,这是一个误解,看到异步编程意味着多线程吗?
好吧,另一个关于发电机的黑客:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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!');
} |
- You say hack,but I'm pretty sure this is the use case of generatiors…(well,except that this relies on EDOCX1 university 0 being the window object)
使用代理:
1 2 3 4
| var a = new Proxy({ i: 0 }, {
get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3); |
代理基本上假装为目标对象(第一个参数),但截取目标对象上的操作(在本例中是"get property"操作),这样就有机会执行默认对象行为以外的操作。在这种情况下,当==强制其类型以将其与每个数字进行比较时,对a调用"get property"操作。这种情况发生了:
我们创建了一个目标对象,{ i: 0 },其中i属性是我们的计数器。
我们为目标对象创建一个代理并将其分配给a。
对于每个a ==比较,a的类型被强制为原始值。
这种类型的强制导致在内部调用a[Symbol.toPrimitive]()。
代理程序使用"get handler"截取获取a[Symbol.toPrimitive]函数。
代理的"get handler"检查正在获取的属性是Symbol.toPrimitive,在这种情况下,它会递增,然后从目标对象返回计数器:++target.i。如果检索到不同的属性,我们只返回默认属性值,target[name]。
所以:
1 2 3 4
| 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 |
与大多数其他答案一样,这只适用于松散的相等检查(==),因为严格的相等检查(===不执行代理可以截取的类型强制。
- 代理的有趣使用
- 不过,使用代理并没有意义——在对象上以同样的方式定义Symbol.toPrimitive也可以。
实际上,问题的第一部分的答案在每种编程语言中都是"是"。例如,在C/C++的情况下:
1 2 3 4 5 6 7
| #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;
} |
- 我认为在每种编程语言中都不可能。例如,并非所有语言都有预处理器。为此,并非所有语言都使用&&表示逻辑"and"。
- 在Python和C++中,我发现了一种使用运算符重载的方法。
- 大多数语言都允许您覆盖操作员
- 请用brainf*ck做同样的事
- 你可以在Java中使用反射和混乱的整数缓存。
- 考虑到std::cout的使用,我非常怀疑C语言中的这种用法。
- 我知道。你应该把它改成printf。但我想你明白了。
- 不能用那些不支持该点突变的语言来做,例如,在haskell中没有可比的语言。
- 我不是haskell的专家,但对于==运算符,使用适当的类型类和适当的重载,这不可能吗?古斯塔沃的例子当然很聪明,但我希望大多数语言(没有处理器)都能通过运算符重载而不是符号重新定义来实现。
- @我试过了。它与Integer.valueOf(1)一起工作。它也能与原始的int一起工作吗?
- @ericduminil codegolf.stackexchange.com/a/28818/51825(使Integer型的a起作用)
- @CAD97:我不这么认为。链接的答案真的很有趣,但还是有点作弊。它解决了Integer.valueOf(2 + 2) == 5而不是2 + 2 == 5的问题。如果我将a设置为整数,它仍然只能等于一个int,不能等于三个不同的整数。
- 问题是询问JavaScript,而不是C++。
- @正如我所说,我是在讨论问题的第一部分。我认为第二部分已经得到了充分的回答。
- 这个问题不仅明确地提到了javascript,而且还标记了它。C/C++完全无关
- @Jasoncarr对不起,我很抱歉回复。它/@dcastor1/wtffffff
- 这在ML中是不可能的。相等测试操作符(op=是硬连线的,表示相等,您不能将它重新定义为表示任何其他内容。此外,语言对值和包含值的可变单元格进行了区分。您可以测试可变单元格是否相等,但这会比较单元格的对象标识,而不是它们的内容。
- @Tim Casterijns:Please check my answer to CPburnz.Also seems others answers got a lot of upvotes talking about the first part of the question,including examples in Java,C§35;;etc.Finally,I'm sorry but irregant it's a legal term,used in court rooms.方案拟订中没有这样的想法,同样的方式也不能作为其他人的愿望提供。Meaning,in programming everything is relevant and possible.
- @Gustavorodr@35;237;Guez also seems others answers got a lot of upvotes talking about the first part of the question,including examples in Java,C@35;yes don't worry,I downvoted all of them.如果你认为"不现实"只是在法院房间里使用,你可能想看看什么是世界的意思。
相同,但不同,但仍然相同(可以多次"测试"):
1 2 3 4 5 6 7 8 9
| 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!');
} |
我的想法是从数字对象类型方程的工作原理开始的。
使用符号的ECMAScript 6答案:
1 2 3
| const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3)); |
由于==的使用,javascript应该将a强制为接近第二个操作数的对象(在本例中为1、2、3)。但在javascript试图自己进行强制之前,它试图调用Symbol.toPrimitive。如果提供Symbol.toPrimitivejavascript,则将使用函数返回的值。否则,javascript将调用valueOf。
我认为这是实现它的最小代码:
1 2 3 4 5
| i=0,a={valueOf:()=>++i}
if (a == 1 && a == 2 && a == 3) {
console.log('Mind === Blown');
} |
使用自定义valueOf创建一个虚拟对象,在每次调用时增加一个全局变量i。23个字!
这一个使用了一个很好的副作用导致全局变量的定义属性!
1 2 3 4 5 6 7 8 9 10 11 12
| var _a = 1
Object.defineProperty(this,"a", {
"get": () => {
return _a++;
},
configurable: true
});
console.log(a)
console.log(a)
console.log(a) |
- 你可以在a上使用一个闭包:get: (a => () => ++a)(0),没有全局必要。
- @尼纳斯霍尔茨当然,但我们在这里谈论的是坏的做法-让我来看看:d
通过在类声明中重写valueOf,可以做到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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},表明条件为真。