Вопрос: Может ли (a == 1 && a == 2 && a == 3) когда-либо оценивать значение true?


Заметка модератора: Пожалуйста, сопротивляйтесь желанию изменить код или удалить это уведомление. Шаблон пробела может быть частью вопроса, и поэтому его не следует подделывать без необходимости. Если вы находитесь в лагере «пробелы - незначителен», вы должны принять код как есть.

Возможно ли, что (a== 1 && a ==2 && a==3)может оценить trueв JavaScript?

Это вопрос интервью, заданный крупной технологической компанией. Это произошло две недели назад, но я все еще пытаюсь найти ответ. Я знаю, что мы никогда не пишем такой код в нашей повседневной работе, но мне любопытно.


2227


источник


Ответы:


Если вы воспользуетесь преимуществами как ==работает , вы можете просто создать объект с пользовательским toString(или valueOf), которая изменяет то, что она возвращает каждый раз, когда она используется так, что она удовлетворяет всем трем условиям.

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, тогда двигатель попытался бы преобразовать строку в число, давая нам тот же конечный результат, хотя и с немного более длинным путем.


3065



Я не мог удержаться - другие ответы, несомненно, верны, но вы действительно не можете пройти мимо следующего кода:

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

Обратите внимание на странный интервал в if(что я скопировал с вашего вопроса). Это полуширина Hangul (это корейская для тех, кто не знаком), который является символом пробела Unicode, который не интерпретируется сценарием ECMA как символ пробела - это означает, что он является допустимым символом для идентификатора. Поэтому есть три совершенно разные переменные: одна с хангулом после 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!")
}


1890



ВОЗМОЖНО!

var i = 0;

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

Это использует геттер внутри withзаявление aоценить три разных значения.

... это все еще не означает, что это должно использоваться в реальном коде ...


560



Пример без геттеров или 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 a = {[Symbol.toPrimitive]: ((i) => () => ++i) (0)};

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


421



Если задано, возможно ли это (не ДОЛЖНО), он может попросить «а» вернуть случайное число. Было бы правдой, если бы он последовательно генерировал 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;
    }
  }
}


245



Когда вы не можете ничего сделать без регулярных выражений:

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

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

Он работает из-за пользовательских valueOfметод, который вызывается при сравнении объекта с примитивным (например, Number). Главный трюк в том, что a.valueOfкаждый раз возвращает новое значение, потому что оно вызывает execна регулярном выражении с gфлаг, который вызывает обновление lastIndexэтого регулярного выражения каждый раз, когда найдено совпадение. Итак, первый раз this.r.lastIndex == 0, он соответствует 1и обновления lastIndex: this.r.lastIndex == 1, поэтому следующее регулярное выражение будет соответствовать 2и так далее.


192



Это может быть выполнено с использованием следующего в глобальном масштабе. Для nodejsиспользование globalвместо windowв приведенном ниже коде.

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

Этот ответ злоупотребляет неявными переменными, предоставляемыми глобальной областью в контексте выполнения, определяя получателя для извлечения переменной.


181



This is possible in case of variable a being accessed by, say 2 web workers through a SharedArrayBuffer as well as some main script. The possibility is low, but it is possible that when the code is compiled to machine code, the web workers update the variable a just in time so the conditions a==1, a==2 and a==3 are satisfied.

This can be an example of race condition in multi-threaded environment provided by web workers and SharedArrayBuffer in JavaScript.

Here is the basic implementation of above:

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

modifier.js

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

On my MacBook Air, it happens after around 10 billion iterations on the first attempt:

enter image description here

Second attempt:

enter image description here

As I said, the chances will be low, but given enough time, it'll hit the condition.

Tip: If it takes too long on your system. Try only a == 1 && a == 2 and change Math.random()*3 to Math.random()*2. Adding more and more to list drops the chance of hitting.


167



This is also possible using a series of self-overwriting getters:

(This is similar to jontro's solution, but doesn't require a counter variable.)

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


139