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

2484

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

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

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

Димпу Аравинд Будда
источник
9
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
deceze
109
Людям, которые, по-видимому, проголосовали за то, чтобы прикрыть это как слишком широкое : это копать в Javascript, говоря, что есть слишком много действительных ответов?
Томсминг
24
Некоторые люди сидят, философствуя о том, что возможно. Другие сосредотачивают свои усилия на том, создают ли они жизнеспособные, бизнес-правильные продукты для своих клиентов. ИМО, этот вопрос не имеет практической пользы, кроме того факта, что вы никогда не должны задавать подобные вопросы в интервью или писать код такого рода. Вот почему это должно быть закрыто. Я имею в виду, понимает ли бизнес, что они заплатили кому-то реальные деньги, чтобы сидеть и говорить об этом?
P.Brian.Mackey
15
После прочтения ответов, мораль рассказа такова: не используйте, ==когда вы имеете в виду ===, имейте стандарт кодирования, который запрещает имена переменных, отличных от ASCII, и используйте процесс линтинга, который обеспечивает соблюдение двух предыдущих моралей.
Джесси С. Слайсер
87
Примечание для модератора: у переполнения стека есть история людей, отвечающих на разных языках ответами на один из них. Они являются попыткой ответить на вопрос , потому что они являются решением общей проблемы, хотя и на другом языке. Пожалуйста, воздержитесь от пометки их как "не ответ". Сказав это, пожалуйста, также воздержитесь от публикации большего количества ответов на разных языках - есть причина, по которой этот вопрос специфичен для JavaScript, как указано в комментариях под некоторыми из этих других ответов, и есть причина, по которой нам нравятся наши языковые вопросы оставаться таким.
BoltClock

Ответы:

3323

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

Кевин Б
источник
70
Можете ли вы достичь этого, изменив подразумеваемую valueOf()операцию?
Стерлинг Арчер
43
Да, valueOf работает вместо toString по той же причине
Кевин Б.
4
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
deceze
13
В соответствии с этим сначала будет пробоваться преобразование чисел, поэтому оно valueOfбудет немного лучше.
Салман А
6
@Pureferret левая часть сравнения на равенство - это объект, а не число. То, что этот объект имеет свойство number i, не беспокоит движок. ;)
Томсминг
2057

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

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

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

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

Не делай этого. Шутки в сторону.

Edit: Он пришел к моему вниманию , что (хотя и не разрешаются начинать переменный) нулевая ширину столяра и нулевой ширина не-столярные символы также разрешены в именах переменных - см запутываний 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!")
}

Джефф
источник
368
Судя по странному интервалу в исходном вопросе, я думаю, что это именно тот ответ, который искал вопрос на собеседовании - использование непробельных символов, которые выглядят как пробелы. Хорошее место!
Баракус
18
@Baracus Это был RonJohn, который заметил странный интервал в своем комментарии к ответу Кевина, который напомнил мне об этой (ужасной) технике, поэтому я не могу поверить, что заметил ее. Я был немного удивлен тем, что никто уже не ответил на этот вопрос, так как он несколько лет назад обошел мою работу из-за сообщения в блоге - я вроде бы предположил, что к настоящему моменту это стало довольно распространенным знанием.
Джефф
102
Конечно, это запрещено как стандартная лазейка , которая также относится к собеседованиям. [нужная цитата]
Sanchises
13
Учитывая исходный интервал, он может быть еще хуже, т. Е. var ᅠ2 = 3Была использована переменная ; так что есть три переменные aᅠᅠ= 1, ᅠ2 = 3, a = 3( a␣ = 1, ␣2 = 3, a = 3так, чтобы (a␣==1 && a==␣2 && a==3)) ...
Хольгер
2
@ AL-zami есть дополнительный символ в двух переменных, который отображается на экране как пробел, но интерпретируется как часть идентификатора, то есть есть три отдельные переменные - a, a и a - дополнительный символ пространство по ширине хангыль.
Джефф
620

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

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

Джонас Уилмс
источник
65
Да, я пробовал то же самое :) Так что правильный ответ в интервью был бы: «Это не может произойти в моем коде, потому что я никогда не использую with».
Заостренный
7
@Pointy - И я программирую в строгом режиме, где withэто не разрешено.
Jfriend00
6
@Pointy в принятом ответе они делают что-то похожее без того, withчтобы это могло произойти
Jungkook
2
@jorrit никто бы не использовал ==. И ===мешает принятому ответу
Jonas Wilms
4
@JonasW. Многие люди до сих пор используют, ==но я не видел withс тех пор ... ну, на самом деле никогда не выходил за рамки документации JS, где написано "пожалуйста, не используйте это". В любом случае, хорошее решение.
wortwart
516

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

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

Это работает, потому что ==вызывает toStringкакие вызовы.join массивы.

Другое решение, использующее Symbol.toPrimitiveES6-эквивалент toString/valueOf:

let i = 0;
let a = { [Symbol.toPrimitive]: () => ++i };

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

оборота георг
источник
9
without valueOfну ... это более косвенно, но в основном то же самое.
Джонас Уилмс
11
Мне действительно нравится это решение, потому что вы не переопределяете ничего, кроме собственной функции соединения объектов, и это просто очень чистый и легко читаемый хак, который делает логику верной.
Алекс Педерсен
28
Честно говоря, я думаю, что это лучший ответ. Это не предполагает ничего необычного, просто устанавливает несколько значений. Очень легко понять даже с базовыми знаниями JS. Отлично сработано.
Зак Делвенталь
14
Это так много смысла, что кажется почти полезным.
Андрей
7
Я знал , что большинство ответов будут о злоупотреблении toStringили , valueOfно это один полностью поймал меня из охраны. Очень умный, и я не знал, что он звонил .joinизнутри, но это имеет смысл.
GBarroso
268

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

ocomfd
источник
102
Я намеренно дал бы этот ответ, даже если бы знал другие решения, потому что он отвечает на вопрос, но, очевидно, не то, что они были после. Играй в глупые игры, выигрывай глупые призы.
ESR
2
Но что, если потребуется более 1000 испытаний?
Пийин
9
@Piyin Если вам потребуется более 1000 испытаний, вы выиграете приз!
Скитс
5
Мне нравится этот ответ, потому что доведение его до крайности говорит о том, что это возможно на любом языке, если регистры / кэш процессора поражены достаточным количеством космических лучей во время работы программы, или если кто-то намеренно выполняет сбой питания, так что ветвь отказа условное условие на самом деле не прыгает.
Ponkadoodle
Самый низкий: 1, самый высокий: 412.
KyleFairns
210

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

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и обновляет lastIndex:, this.r.lastIndex == 1так что в следующий раз регулярное выражение будет соответствовать2 и так далее.

Кос
источник
22
@Abdillah объект регулярного выражения запомнит последний индекс, execкоторому он соответствует, повторный вызов начнет поиск по этому индексу. МДН не очень понятно.
Симон Чан
Я вижу, поэтому this.rобъект регулярного выражения запоминает состояние / индекс. Спасибо!
Абдилла
Я бы порекомендовал передать строку, execхотя, а не целое число для преобразования в строку .
Берги
используйте регулярные выражения, и теперь у вас есть две проблемы
Алексей Соловей
191

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

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

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

jontro
источник
Это предполагает, aчто это свойство, thisкоторым оно не является. Если бы aбыла локальная переменная (как это выглядит), то это не сработало бы.
Jfriend00
1
@ jfriend00 вы имеете в виду, если вы поместили var a; где-то?
Jontro
Да. Ссылка a == 1подразумевает, aчто где-то есть переменная, а не свойство this. Хотя есть странное место, такое как глобальные переменные, где оба могут быть истинными, обычно объявление переменной с помощью var aили let aозначает, что нет this, позволяет вам обращаться aкак к свойству, как предполагает код. Итак, ваш код, очевидно, предполагает некоторую странную глобальную переменную. Например, ваш код не работает в node.js и не работает в строгом режиме внутри функции. Вы должны указать точные обстоятельства, где это работает, и, вероятно, объяснить, почему это работает. В противном случае это вводит в заблуждение.
jfriend00
@ Jfriend00 ну конечно. Не уверен, что это добавит гораздо большую ценность в сочетании с другими уже ответами.
Обновлю
14
Вопрос был в том, могло ли это когда-либо быть правдой. И ответ - да, и это один из сценариев, где это может быть правдой: aэто не локальная переменная, а определяется в глобальной области с возрастающим геттером.
Зак Делвенталь
190

Это возможно в случае, когда к переменной aобращаются, скажем, 2 веб-работника через SharedArrayBuffer, а также некоторый основной скрипт. Возможность невелика, но вполне возможно , что , когда код компилируется в машинный код, веб - рабочие обновить переменную 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'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
    })
})

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

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

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

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

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

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

mehulmpt
источник
50
Честно говоря, это лучший ответ. Все остальные ответы требуют преднамеренной попытки сделать что-то глубоко не интуитивное. Этот ответ на самом деле отражает то, что может произойти в реальном мире - состояние гонки.
Том Свирли
34
Мало того - я действительно видел, как это происходит в реальном мире. Не с точным условием в вопросе, но, конечно, с проверкой (a == 1) в начале функции и (a == 2) позже в функции, и с тем, чтобы код достиг обоих условий. К вашему сведению, в первый раз я увидел это в контроллере двигателя автомобиля, и мы установили стандарты кодирования. Во второй раз я использовал систему разбрасывания осколков и факелов для военных самолетов, и в первый же день в компании я обнаружил это и исправил его, пока остальная часть команды все еще обсуждала проблему. (Уровень благодарности: высокий! :)
Грэм
38
Итак, вы работали над "контроллерами автомобильных двигателей" и "системами дозирования мусора и вспышек", которые запрограммированы в javascript для веб-работников? Я не думаю, что я снова выйду на улицу.
psaxton
12
@psaxton :) Конечно нет - но у нас есть многопоточное программное обеспечение с общими данными. Это антишаблон для всего многопоточного программного обеспечения, не относящийся только к Javascript или веб-работникам. Неважно, программируете ли вы на ассемблере, Brainf * ck, Visual BASIC, C или Javascript - если вы сделаете это с общими данными в многопоточном приложении, это всегда будет неудачным.
Грэм
4
Я думаю, что теперь это тщательно продуманная оболочка ответа @ jontro.
qntm
148

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

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

Патрик Дарк
источник
61
Обратите внимание, что подход использования геттера также работает ===, а не только ==.
Макьен,
Это решение основано на thisтом, чтобы быть глобальным объектом внутри тела функции стрелки.
Рой Тинкер
@Midnightas Я бы не классифицировал любые другие ответы как «код пирамиды» .
Патрик Робертс
Обратите внимание, это также работает с произвольным порядком, не так ли? Нравится (a == 3 && a == 2 && a == 1)?
Йоханнес
131

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

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

Нина Шольц
источник
5
только function A() {value = 0;в начале?
Дэйв С
valueOfпереопределяется, this method is usually called automatically by JavaScript behind the scenes, and not explicitly in codeпоэтому, когда мы сравниваем значение, оно фактически увеличивает ..
Даньял Сандиело
130

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

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

Вы можете заметить небольшое расхождение со вторым, но первый и третий идентичны невооруженным глазом. Все 3 разных персонажа:

a- латинская строчная A
- полная латинская строчная A
а- кириллическая строчная A

Общий термин для этого - «гомоглифы»: разные символы юникода, которые выглядят одинаково. Обычно трудно получить три , которые совершенно неразличимы, но в некоторых случаях вам может повезти. A, Α, А и Ꭺ будут работать лучше (латиница-A, греческая альфа , кириллица-A , и Cherokee-A соответственно, к сожалению, греческие и чероки строчные буквы слишком отличаются от латинского a: α, и так Безразлично помогите с приведенным фрагментом).

Существует целый класс атак на гомоглифы, чаще всего в поддельных доменных именах (например, wikipediа.org(кириллица) против wikipedia.org(латиница)), но он также может отображаться в коде; обычно упоминается как закулисный (как упомянуто в комментарии, [закулисные] вопросы теперь не по теме PPCG , но раньше это был тип проблемы, когда такие вещи появлялись ). я использовал этот сайт, чтобы найти гомоглифы, используемые для этого ответа.

Draco18s больше не доверяет SE
источник
19
«Небольшое несоответствие» - это не то, как я бы это назвал.
4
@hvd Полностью зависит от вашего рендеринга шрифтов. Это то, что я вижу .
Draco18s больше не доверяет SE
1
@ Джейк Да, строчная латинская буква A полной ширины не самый лучший гомоглиф (но варианты заглавных букв удивительны). Как правило, хотя вам нужно только два, чтобы получить желаемый эффект.
Draco18s больше не доверяет SE
@ Draco18s Согласовано: обычно требуется только 2. Хорошая работа на дополнительную информацию тоже!
JakeSteam
10
Вы также можете использовать вариант выбора Unicode (U + FE00..U + FE0F). Ни один из них не являются a: a︀ a︁ a︂. Больше не нужно беспокоиться о расхождениях.
Салман А
108

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

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

Если вы просто видите верхнюю часть моего кода и запускаете его, вы говорите WOW, как?

Поэтому я думаю, что достаточно сказать Да, это возможно для того, кто сказал вам: нет ничего невозможного

Уловка: я использовал скрытый символ после, 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!😎");
    }
}

Live Demo

баран
источник
56
Версия javascript - это настоящее преступление против человечества, и способность делать это должна быть незаконной в соответствии с конвенциями ООН. Я думаю, что пришло время очистить мир от всех знаний javacript.
уточнение
2
Объявление функции может быть еще короче. if‌=()=>!0
Форивин
4
С какой стати ты это использовал document.write? Это верный способ не получить работу независимо от остальной части ответа.
Цербр
3
@Cerbrus, спасибо за вашу заметку. Сначала я написал свой ответ, console.logно изменил его на document.write. Действительно, я всегда использую console.logв своих кодах, но здесь я просто хочу показать текст пользователям в поле фрагмента кода StackOverflow. Поэтому я хотел показать свое сообщение более красивым, чем сообщение, сгенерированное console.log. Нажмите Run Code Snippetкнопку моего ответа и других ответов. Фрагмент кода SO позволил мне использовать html, JS и CSS, а затем я захотел использовать его в своем ответе и сделать его красивым. Я думаю, что это не имеет никакого отрицательного побочного эффекта и не сделало мой ответ большим или законченным.
RAM
1
@Clearer, если бы конвенции ООН могли эффективно изменить мир, тогда у нас должен быть мир лучше, чем этот. Нам нужно нечто большее, чем заявление в ООН, и до этого дня я думаю, что мы можем использовать этот мой трюк с Javascript;)
RAM
97

JavaScript

a == a +1

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

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

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

Правда, это не совсем то, что спросил интервьюер (это не работает с a=0 ), но это не связано с уловкой со скрытыми функциями или перегрузкой операторов.

Другие языки

Для справки, есть a==1 && a==2 && a==3решения на Ruby и 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!")

Ява

Можно изменить Integerкэш Java :

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.");
        }
    }
}
Эрик Думинил
источник
27
@ cᴏʟᴅsᴘᴇᴇᴅ: Java, Javascript, potayto, potahto :) Хороших JS-ответов уже достаточно. Я просто подумал, что было бы интересно показать, как это можно сделать на других языках, и, возможно, дать разработчикам JS некоторые идеи.
Эрик Думинил
2
@ cᴏʟᴅsᴘᴇᴇᴅ: обновлено на примере JS.
Эрик Думинил
1
Почему Java-версия не работает Integer a = 42(или не работает)? Как я понимаю, автобокс, Integer a = 42; a == 1 && a == 2 && a == 3должен боксировать все ints. Или это распаковать для сравнения?
CAD97
@ CAD97: Integer == intкажется, приводит к распаковке. Но используя Integer#equals(int)силы автобокса, так оно и работает. Спасибо за комментарий!
Эрик Думинил
@StephanBijzitter: Пожалуйста, объясните. Насколько я знаю, есть только Numbersв JS, которые в основном похожи на doubles. Они могут выглядеть как целые числа, и вы можете использовать их как целые числа, но они все еще не являются целыми числами. Я не думаю, что n == n + 1когда-либо может быть правдой для целых чисел в Java / Python / C / Ruby / ...
Эрик Думинил
80

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

* Этот ответ можно упростить, если использовать несоединение с нулевой шириной (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).

Салман А
источник
75

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

Нет необходимости для скрытого обмана персонажа.

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

MonkeyZeus
источник
6
Уч. __defineGetter__на самом деле не является частью языка JS, просто уродливая версия defineProperty. typeofэто не функция, и это необъявленное iпросто ужасно. Все еще, кажется, стоит 40 голосов: /
Джонас
6
@JonasW. 41 upvotes :-) Я знаю, что __defineGetter__не рекомендуется для developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… но он явно выполняется в моем FireFox v 57.0.4, поэтому я решил показать это вместо defineProperty()потому что унаследованный код реален и не может быть проигнорирован. Независимо от уродства, декларация, iкак я это сделал, является хорошо известным / задокументированным поведением. Может быть, я был просто в настроении PCG ¯ \ _ (ツ) _ / ¯
MonkeyZeus
68

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

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

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

Фрэнк У. Замметти
источник
13
Вопрос (или все вопросы интервью), вероятно, должен проверить готовность кандидатов думать о проблеме, особенно о тех, которые «очевидно очевидны», как эта. Кто-то, кто отказывается думать, потому что они верят, что «знают» ответ - это не хороший найм.
Шамму
5
@ Дон Хэтч Нет, я бы не стал их наказывать, если бы они ответили добросовестно, особенно если бы они дали правильный ответ, как это показали другие ... но я бы тогда попросил продолжение, чтобы попытаться выяснить, считают ли они это хороший способ написать код или нет. Быть знающим и уметь придумывать «правильный» ответ - это лишь часть того, чтобы быть хорошим разработчиком. Гораздо более важным для «профессионального» разработчика является написание кода, понятного и поддерживаемого в будущем, часто менее способными разработчиками. Чрезмерно умные разработчики так же плохи, как и неспособные IME.
Фрэнк У. Замметти
16
Это не отвечает на вопрос.
TylerH
6
Грустная вещь об этом ответе состоит в том, что вчерашний пользователь 1rep ответил ему 2 раза и получил 2 отзыва, в результате чего он удалил этот вопрос.
Джонас Уилмс
8
@JohnColeman вопрос спрашивает, как код может оценить как истинное. Он не задает причины, по которым интервьюер предложил вопрос в первую очередь. Этот ответ даже не пытается ответить на вопрос, который задают, и вместо этого полностью сосредоточен на «что я буду делать» версии попытки угадать, какова была цель интервьюера. Если бы этот вопрос был задан, он был бы слишком широким. Поэтому этот ответ не принадлежит ни здесь, ни где-либо на сайте.
TylerH
43

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

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

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

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

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

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

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

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

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

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

Дирк Воллмар
источник
1
Тогда вопрос становится слишком широким. Различные языки могут реализовать это с различной степенью легкости. Вопрос набрал столько сил, потому что это вопросы и ответы JS, но это только мой 2c.
cs95
1
Причины разные C # и JavaScript, поэтому этот ответ не является законным.
Эдвин
3
@ Эдвин: Причины абсолютно одинаковы: Unicode возится с похожими символами или пробелами, условиями гонки или побочными эффектами операции сравнения (последняя показана в моем примере).
Дирк Воллмар
2
@ cᴏʟᴅsᴘᴇᴇᴅ: Иногда взгляд на вещи под более широким углом помогает увидеть реальную проблему.
Дирк Воллмар
3
Я хотел бы, чтобы этот ответ мог быть помечен на этот вопрос некоторым мета-способом. Прочитав все ответы выше, я почувствовал, что у JS так много дыр, но вы просто суммировали все ответы за один раз. И вы сделали это таким образом, чтобы превратить это в звездный вопрос для собеседования (если убран языковой тег), по моему мнению. Браво!
KCE
41

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

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

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

Теофиль
источник
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!');
}

BaggersIO
источник
Вы говорите хак, но я почти уверен, что это случай использования генераторов ... :) (хорошо, за исключением того, что это зависит от thisтого, чтобы быть оконным объектом)
Коди G
29

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

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»), так что есть возможность сделать что-то отличное от поведения объекта по умолчанию. В этом случае действие «get property» вызывается aпри приведении ==его типа для сравнения его с каждым числом. Это случилось:

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

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

IceCreamYou
источник
2
Однако в этом нет никакого смысла использовать прокси-сервер - Symbol.toPrimitiveточно так же можно определить и объект.
Ry-
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;
}
Густаво Родригес
источник
27
Я не думаю, что это возможно на каждом языке программирования. Например, не все языки имеют препроцессоры. В этом отношении не все языки используют &&для логических «и».
Кит Томпсон
3
Я нашел способ, который работает как в Python, так и в C ++, который использует перегрузку операторов.
Дональд Дак
7
И вы можете сделать это в Java, используя отражение и испорчивая целочисленный кеш.
CAD97
7
Не может сделать это на языках, которые не поддерживают мутации в этом месте, например, в haskell нет ничего похожего
Джейсон Карр
4
Вопрос заключается в JavaScript, а не в C ++.
Все работники необходимы
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!');
}

Моя идея началась с того, как работает уравнение типа объекта Number.

Preda7or
источник
4
Работает и во второй раз!
Салман А
25

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

Омар Альшакер
источник
24

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

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

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

Создание фиктивного объекта с пользовательским элементом, valueOfкоторый увеличивает глобальную переменную iпри каждом вызове. 23 персонажа!

GAFI
источник
14

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

var _a = 1

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

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

Бен Обин
источник
8
вы можете использовать замыкание поверх a: get: (a => () => ++a)(0),глобально не нужно.
Нина Шольц
13
@NinaScholz конечно, но мы говорим о плохих практиках здесь - просто позвольте мне иметь это: D
Бен Обин
1

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

Джонатан Куль
источник