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,那么引擎会尝试将字符串转换为给出相同结果的数字,但路径稍长.
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)
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)
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)
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)
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,依此类推.
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 == 2并Math.random()*3改为Math.random()*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来滥用执行上下文中的全局作用域提供的隐式变量.
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)
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)
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上是偏离主题的,但过去常常会出现这种类型的挑战).我用这个网站找到了用于这个答案的同形词.
RAM*_*RAM 104
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#版本(增加属性值技术):
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)
Eri*_*nil 97
在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 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)
Sal*_*n A 79
这是一个倒置的版本@杰夫的回答*其中隐藏字符(U + 115F,U + 1160或U + 3164)用于创建看起来像变量1,2和3.
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)
Fra*_*tti 68
但老实说,是否有办法让它评估为真或不(正如其他人所表明的那样,有多种方式),我要寻找的答案,作为进行了数百次访谈的人,将会是类似的东西:
"好吧,也许是在一些奇怪的情况下,对我来说并不是很明显......但如果我在实际代码中遇到这个问题,那么我会使用常见的调试技术来弄清楚它是如何以及为什么要做它正在做的事情然后立即重构代码以避免这种情况...但更重要的是:我绝对不会首先编写该代码,因为这是复杂代码的定义,我努力永远不会编写复杂的代码".
我猜一些采访者会冒犯一个显然意味着一个非常棘手的问题,但我不介意有意见的开发人员,特别是当他们能够用合理的思想支持它并且可以将我的问题与我的问题相吻合时关于自己的有意义的陈述.
Dir*_*mar 43
如果您遇到过这样的面试问题(或者在代码中注意到一些同样出乎意料的行为),请考虑哪些事情可能会导致乍一看似乎不可能的行为:
编码:在这种情况下,您正在查看的变量不是您认为的变量.这可能发生,如果你有意与使用Unicode勾搭同形字或空格字符,使一个变量看起来像一个又一个的名字,但也可以不小心将编码的问题,例如,从包含意外的Unicode编码的网络复制和粘贴代码时点(例如,因为内容管理系统做了一些"自动格式化",例如用flUnicode'LATIN SMALL LIGATURE FL'(U + FB02)替换.
竞争条件:可能发生竞争条件,即代码未按开发人员预期的顺序执行的情况.竞争条件经常发生在多线程代码中,但多线程不是竞争条件可能的要求 - 异步性就足够了(不要混淆,异步并不意味着在引擎盖下使用多个线程).
请注意,因为它是单线程的,因此JavaScript也不会没有竞争条件.请参阅此处获取简单的单线程 - 但异步 - 示例.在单个语句的上下文中,竞争条件在JavaScript中很难实现.
与Web worker的JavaScript有点不同,因为您可以拥有多个线程.@mehulmpt向我们展示了使用网络工作者的一个很好的概念验证.
副作用:相等比较操作的副作用(不必像在这里的例子中那样明显,通常副作用非常微妙).
这些问题可以出现在许多编程语言中,而不仅仅是JavaScript,因此我们没有看到这里的经典JavaScript WTF之一1.
当然,面试问题和这里的样本都看起来非常人为.但它们是一个很好的提醒:
1举例来说,你可以找到在一个完全不同的编程语言(C#)的例子显示出副作用(一个明显的例子)在这里.
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"动作,以便将其与每个数字进行比较.有时候是这样的:
{ i: 0 }其中i属性是我们的计数器aa ==比较,将as的类型强制转换为原始值a[Symbol.toPrimitive]()内部调用a[Symbol.toPrimitive]使用"获取处理程序"获取函数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)
与大多数其他答案一样,这仅适用于松散的相等检查(==),因为严格的相等检查(===)不执行代理可以拦截的类型强制.
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)
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)
我的想法从数字对象类型方程的工作原理开始.
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)
| 归档时间: |
|
| 查看次数: |
363252 次 |
| 最近记录: |