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

2020/09/14 15:31 · javascript ·  · 0评论

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

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

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

如果你趁如何==工作,你可以简单地创建一个自定义的对象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,则引擎将尝试将该字符串转换为一个数字,使我们得到相同的最终结果,尽管路径略长。

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

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

有可能的!

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.");
  }

没有吸气剂或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);

如果询问是否可能(不是必须),则可以询问“ 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;
    }
  }
}

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

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,依此类推。

可以在全局范围内使用以下内容来实现。为了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例如,两个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在列表中添加越来越多的内容会降低命中率。

也可以使用一系列自动覆盖的吸气剂:

(这类似于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.");
    }
})();

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

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!');
}

我没有看到这个答案已经发布,所以我也将这个答案也放进去了。这类似于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上已不在主题之,但过去曾是挑战,这类事情会在此出现)。我使用此网站找到用于此答案的同形文字。

是的,有可能!😎

»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;}

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

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

窍门:之后,我使用了一个隐藏字符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!😎");
    }
}

现场演示

的JavaScript

a == a +1

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

这意味着,如果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.");
        }
    }
}

这是一个倒置的版本@杰夫的回答 *其中隐藏字符(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)。

面试的第一规则;永远不要说不可能。

无需隐藏角色欺骗。

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?' );
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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!');
}

使用代理

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

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

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

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

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对象类型方程的工作原理开始。

使用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

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

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

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

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

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

var _a = 1

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

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

通过覆盖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。

本文地址:http://javascript.askforanswer.com/a-1-a-2-a-3keyipingguweizhenma.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!