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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

ЭТО ВОЗМОЖНО!

var i = 0;

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

Ответ 4

Пример без геттеров или значения:

a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

Это возможно в случае обращения к переменной a, скажем, 2 веб-работникам через SharedArrayBuffer, а также некоторые основные script. Возможность низкая, но возможно, что когда код компилируется в машинный код, рабочие сети обновляют переменную a как раз вовремя, поэтому выполняются условия a==1, a==2 и a==3.

Это может быть пример состояния гонки в многопоточной среде, предоставляемой веб-работниками и SharedArrayBuffer в JavaScript.

Вот базовая реализация выше:

main.js

// Main Thread

const worker = new Worker('worker.js')
const modifiers = [new Worker('modifier.js'), new Worker('modifier.js')] // Let 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
    })
})

На моем MacBook Air это происходит после примерно 10 миллиардов итераций с первой попытки:

введите описание изображения здесь

Вторая попытка:

введите описание изображения здесь

Как я уже сказал, шансы будут низкими, но, учитывая достаточное время, он попадет в состояние.

Совет. Если в вашей системе слишком много времени. Попробуйте только a == 1 && a == 2 и измените Math.random()*3 на Math.random()*2. Добавление все большего количества в список снижает вероятность удара.

Ответ 8

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

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

Ответ 9

Это также возможно с помощью серии самонастраиваемых геттеров:

(Это похоже на решение 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.");
    }
})();

Ответ 10

Я не вижу, что этот ответ уже отправлен, поэтому я тоже заброшу его в микс. Это похоже на ответ Джеффа с пространством хангула полуширины.

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

Ответ 11

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

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

var a = new A;

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

Ответ 12

Да, это возможно! 😎

"JavaScript

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

if‌(a==1 && a== 2 && a==3)
{
    document.write("<h1>Yes, it is possible!😎</h1>")
}

Ответ 13

JavaScript

a == a +1

В JavaScript нет целых чисел, но только Number s, которые реализованы как числа с плавающей запятой двойной точности.

Это означает, что если число a достаточно велико, его можно считать равным трем целым целым числам:

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

Ответ 14

Это инвертированная версия @Jeff answer *, где скрытый символ (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 );

Ответ 15

Правило номер один из интервью; никогда не сказать невозможным.

Не нужно скрывать трюки персонажа.

window.__defineGetter__( 'a', function(){
    if( typeof i !== 'number' ){
        // define i in the global namespace so that it not lost after this function runs
        i = 0;
    }
    return ++i;
});

if( a == 1 && a == 2 && a == 3 ){
    alert( 'Oh dear, what have we done?' );
}

Ответ 16

Честно говоря, есть ли способ оценить его истинным или нет (и, как показали другие, существует несколько способов), ответ, который я бы искал, выступая как человек, который провел сотни интервью, было бы чем-то вроде:

"Ну, может быть, да при некоторых странных условиях, которые мне сразу не очевидны... но если бы я столкнулся с этим в реальном коде, я бы использовал общие методы отладки, чтобы выяснить, как и почему он делает то, что он делал, а затем сразу же рефакторировал код, чтобы избежать этой ситуации... но что более важно: я бы НИКОГДА не писал этот код в первую очередь, потому что это само определение свернутого кода, и я стараюсь никогда не писать сложный код".

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

Ответ 17

Если вы когда-нибудь зададите такой вопрос интервью (или обратите внимание на какое-то столь же неожиданное поведение в вашем коде), подумайте о том, какие вещи могут привести к поведению, которое выглядит на первый взгляд невозможным:

  1. Кодировка: в этом случае переменная, на которую вы смотрите, не та, которую вы считаете. Это может произойти, если вы намеренно возитесь с Unicode с помощью гомоглифов или пробелов, чтобы сделать имя переменной похожим на другое, но проблемы с кодировкой также могут быть введены случайно, например, при копировании и вставке кода из Интернета, который содержит неожиданный код Unicode (например, из-за того, что система управления контентом выполняла некоторые "автоматическое форматирование", такие как замена fl на Unicode "LATIN SMALL LIGATURE FL" (U + FB02)).

  2. Условия гонки: может возникнуть состояние гонки, то есть ситуация, когда код не выполняется в последовательности, ожидаемой разработчиком. Условия гонки часто бывают в многопоточном коде, но несколько потоков не являются обязательным условием для условий гонки. Асинхронность достаточна (и не смущайтесь, асинхронность не означает, что под капотом используются несколько потоков).

    Обратите внимание, что поэтому JavaScript также не свободен от условий гонки только потому, что он однопоточный. См. Здесь простой однопоточный, но асинхронный пример. В контексте одного заявления условие гонки, однако, было бы довольно трудно попасть в JavaScript.

    JavaScript с веб-работниками немного отличается, так как вы можете иметь несколько потоков. @mehulmpt показал нам отличное доказательство концепции с использованием веб-работников.

  3. Побочные эффекты: побочный эффект операции сравнения равенства (который не должен быть столь же очевидным, как в примерах здесь, часто побочные эффекты очень тонкие).

Такого рода вопросы может появляться во многих языках программирования, а не только JavaScript, поэтому мы не видим один из классического JavaScript WTFs здесь 1.

Конечно, вопрос интервью и образцы здесь все выглядят очень надуманными. Но они являются хорошим напоминанием о том, что:

  • Побочные эффекты могут стать действительно неприятными и что хорошо продуманная программа должна быть свободна от нежелательных побочных эффектов.
  • Многопоточное и изменяемое состояние может быть проблематичным.
  • Неправильное кодирование символов и правильная обработка строк могут привести к неприятным ошибкам.

1 Например, вы можете найти пример в совершенно другом языке программирования (С#) экспонирование побочного эффекта (очевидный) здесь.

Ответ 18

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

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

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

Ответ 19

Хорошо, еще один взлом с генераторами:

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

Ответ 20

Использование прокси:

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 == принуждает его тип, чтобы сравнить его с каждым номером. Бывает:

  1. Мы создаем целевой объект { i: 0 }, где свойство i является нашим счетчиком
  2. Мы создаем прокси для целевого объекта и присвоить его a
  3. Для каждого a == сравнения, тип приводятся к примитивному значению a
  4. a[Symbol.toPrimitive]() этого типа приводит к внутреннему вызову a[Symbol.toPrimitive]()
  5. Прокси перехватывает получение функции a[Symbol.toPrimitive] используя "обработчик получения"
  6. Прокси-обработчик "get" проверяет, что полученное свойство имеет вид 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

Как и с большинством других ответов, это работает только с произвольной проверкой равенства (==), потому что строгие проверки на равенство (===) не приводят к принуждению типов, которое может перехватить Прокси.

Ответ 21

На самом деле ответ на первую часть вопроса "Да" на каждом языке программирования. Например, это относится к C/С++:

#define a   (b++)
int b = 1;
if (a ==1 && a== 2 && a==3) {
    std::cout << "Yes, it possible!" << std::endl;
} else {
    std::cout << "it impossible!" << std::endl;
}

Ответ 22

То же, но другое, но все же одно (может быть "проверено" несколько раз):

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

Ответ 23

Ответ ECMAScript 6, в котором используются символы:

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.toPrimitive. Если вы предоставите Symbol.toPrimitive JavaScript, используйте значение, возвращаемое вашей функцией. Если нет, JavaScript будет вызывать valueOf.

Ответ 24

Я думаю, что это минимальный код для его реализации:

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

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

Ответ 25

Этот использует свойство defineProperty с хорошим побочным эффектом, вызывающим глобальную переменную!

var _a = 1

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

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

Ответ 26

Основной причиной, почему это может быть равным, является оператор ==. С === это не сработает.

const a = {
    i: 0;
    valueOf: function() {
       return this.i++;
    }
}

if(a == 1 && a == 2 && a == 3) // true

Поскольку методы valueOf и toString будут вызываться, когда используются операторы ==.

if(a === 1 && a === 2 && a === 3) // false

Методы valueOf и toString не вызываются.

Ответ 27

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

Ответ 28

Это хороший вопрос. Я также хотел добавить реализацию Java, потому что обновление valueOf() в основном переопределяет функцию базового класса. Мне потребовалось несколько минут, чтобы понять, как это работает в JS, поэтому я сопоставил его с Java для понимания.

public class TestClass {

    int value;

    TestClass() {
        this.value = 0;
    }

    @Override
    public String toString() {
        System.out.println("current " + this.value);
        this.value++;
        return String.valueOf(this.value);
    }
}

class CallingClass {

    public static void main(String[] args) {

        TestClass object = new TestClass();
        if (object.toString().equals("1") && object.toString().equals("2") && object.toString().equals("3")) {
            System.out.print("it here");
        }
    }
}

Ответ 29

попробуй это :
То же самое, но другое, но все же (можно "проверить" несколько раз):

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

Ответ 30

Даже без путаницы именования, перегрузки или случайных переменных a == 1 && a == 2 && a == 3 может возвращать true в многопоточных средах, так как значение a может меняться между каждым сравнением, если оно не является потокобезопасным.