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

2203

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

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

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

  • 8
    Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
  • 2
    обратите внимание, что даже если пробелы исправлены, для оригинала все еще возможно использование невидимых символов и, по-видимому, правильное расстояние. (aᅠ== 1 && a == 2 &&ᅠa == 3) (вы просто чередуетесь с реальными пробелами)
Показать ещё 15 комментариев
Теги:
ecmascript-6

29 ответов

3324
Лучший ответ

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

  • 65
    Можете ли вы добиться этого, изменив подразумеваемую операцию valueOf() ?
  • 40
    Да, valueOf работает вместо toString по той же причине
Показать ещё 17 комментариев
1954

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

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 script как символ пробела - это означает, что он является допустимым символом для идентификатора. Поэтому есть три совершенно разные переменные: одна с хангулом после a, одна с ней до и последняя с просто a. Заменяя пространство с помощью _ для удобства чтения, тот же код будет выглядеть так:

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

Отметьте подтверждение проверки валидатора переменных Mathias. Если бы этот странный интервал был фактически включен в их вопрос, я уверен, что это намек на такой ответ.

Не делай этого. Серьезно.

Изменить: мне пришло в голову, что (хотя и не разрешено запускать переменную) Zero-width joiner и Имена символов с нулевой шириной допускаются также в именах переменных - см. Обфускация 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!")
}
  • 352
    Судя по странному интервалу в исходном вопросе, я думаю, что это именно тот ответ, который искал вопрос на собеседовании - использование непробельных символов, которые выглядят как пробелы. Хорошее место!
  • 17
    @Baracus Это был RonJohn, который заметил странный интервал в своем комментарии к ответу Кевина, который напомнил мне об этой (ужасной) технике, поэтому я не могу поверить, что заметил ее. Я был немного удивлен тем, что никто уже не ответил на это, так как он несколько лет назад обошел мою работу из-за сообщения в блоге - я вроде бы предположил, что к настоящему моменту это стало общеизвестным.
Показать ещё 20 комментариев
555

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

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.");
  }
  • 61
    Да, я пробовал то же самое :) Так что правильный ответ в интервью был бы: «Это не может произойти в моем коде, потому что я никогда не использую with ».
  • 0
    @pointy with определенно ошибка дизайна.
Показать ещё 10 комментариев
428

Пример без геттеров или 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);
  • 8
    without valueOf , ну ... это более косвенный, но в основном то же самое.
  • 9
    Мне очень нравится это решение, потому что вы не переопределяете ничего, кроме собственной функции соединения объектов, и это просто очень чистый и легко читаемый хак, который делает логику верной.
Показать ещё 4 комментария
256

Если задано, возможно ли (НЕ ДОЛЖНО), он может попросить "а" вернуть случайное число. Было бы правдой, если бы он последовательно генерировал 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;
    }
  }
}
  • 94
    Я намеренно дал бы этот ответ, даже если бы знал другие решения, потому что он отвечает на вопрос, но, очевидно, не то, что они были после. Играй в глупые игры, выигрывай глупые призы.
  • 2
    Но что, если потребуется более 1000 испытаний?
Показать ещё 4 комментария
210

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

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 и т.д.

  • 9
    Хм .. как это работает?
  • 22
    @Abdillah объект регулярного выражения запомнит последний индекс, которому он соответствует, вызов exec снова начнет поиск по этому индексу. МДН не очень понятно.
Показать ещё 5 комментариев
185

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

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

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

  • 0
    Это предполагает, a свойство является this чем оно не является. Если бы a было локальной переменной (как это выглядит), то это не сработало бы.
  • 1
    @ jfriend00 вы имеете в виду, если вы поместили var a; где-то?
Показать ещё 4 комментария
170

Это возможно в случае обращения к переменной 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 миллиардов итераций с первой попытки:

Изображение 2052

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

Изображение 2053

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

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

  • 48
    Честно говоря, это лучший ответ. Все остальные ответы требуют преднамеренной попытки сделать что-то глубоко не интуитивное. Этот ответ фактически отражает то, что может произойти в реальном мире - состояние гонки.
  • 33
    Мало того - я действительно видел, как это происходит в реальном мире. Не с точным условием в вопросе, но, конечно, с проверкой (a == 1) в начале функции и (a == 2) позже в функции, и с тем, чтобы код достиг обоих условий. К вашему сведению, в первый раз я увидел это в контроллере двигателя автомобиля, и мы установили стандарты кодирования. Во второй раз я использовал систему разбрасывания мусора и вспышек для военных самолетов, и в первый же день в компании я обнаружил это и исправил его, пока остальная часть команды все еще обсуждала проблему. (Уровень благодарности: высокий! :)
Показать ещё 15 комментариев
136

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

(Это похоже на решение 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.");
    }
})();
  • 59
    Обратите внимание, что подход использования геттера также работает с === , а не только с == .
  • 22
    Обратите внимание, что это также работает с меньшим отступом.
Показать ещё 4 комментария
132

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

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

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

a - латинский нижний регистр A
- Полная ширина латинского нижнего регистра A
а - кириллический нижний регистр A

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

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

  • 18
    «Небольшое несоответствие» - это не то, как я бы это назвал.
  • 4
    @hvd Полностью зависит от вашего рендеринга шрифтов. Это то, что я вижу .
Показать ещё 4 комментария
121

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

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!');
}
  • 4
    просто function A() {value = 0; в начале?
  • 0
    valueOf переопределяется, this method is usually called automatically by JavaScript behind the scenes, and not explicitly in code поэтому, когда мы сравниваем значение, оно фактически увеличивает ..
94

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), но это не связано с трюком со скрытыми функциями или перегрузкой оператора.

Другие языки

Для справки, в Ruby и Python есть a==1 && a==2 && a==3 решения. С небольшой модификацией это также возможно на 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.");
        }
    }
}
  • 12
    Код гольф SE кто-нибудь?
  • 26
    Вопрос помечен JS, хотя?
Показать ещё 13 комментариев
83

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

"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, как?

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

Trick: Я использовал скрытый символ после if как сделать функцию, чтобы ее имя было похоже на if. В JavaScript мы не можем переопределять ключевые слова, поэтому я вынужден использовать этот способ. Это подделка, if, но она работает для вас в этом случае!


" С#

Также я написал версию С# (с повышением стоимости свойства):

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

Демо-версия

  • 0
    function if<200c> { } как она работает, почему не работает с function if<123c>{}
  • 0
    @muthukumar, я использовал \u200c (ZERO WIDTH NON-JOINER) в своем ответе, вы хотите, if с \u123c ? OK, напишите, if затем нажмите Alt , нажмите + нажмите 123c отпустите Alt сделайте это слово », ifሼ
Показать ещё 13 комментариев
75

Это инвертированная версия @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 );

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

  • 0
    Также вариант моего ответа , поэтому есть +1. Я также получил несколько необъяснимых отрицательных голосов.
75

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

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

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?' );
}
  • 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 ¯ \ _ (ツ) _ / ¯
66

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

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

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

  • 1
    Итак, не могли бы вы оштрафовать кого-либо, кто добросовестно ответит на ваш вопрос и настолько осведомлен, что сможет ответить на него правильно? Я был бы очень расстроен, если бы кто-то сделал это со мной. Не уверен, что это ваше намерение, но если это не так, вы можете изменить свой ответ, чтобы прояснить его.
  • 13
    Вопрос (или все вопросы интервью), вероятно, состоит в том, чтобы проверить готовность кандидатов думать о проблеме, особенно о тех, которые «очевидно очевидны», как эта. Кто-то, кто отказывается думать, потому что они верят, что «знают» ответ - это не хороший найм.
Показать ещё 11 комментариев
39

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 1
    Тогда вопрос становится слишком широким. Различные языки могут реализовать это с различной степенью легкости. Вопрос приобрел большую популярность, потому что это вопросы и ответы JS, но это только мой 2c.
  • 1
    Причины разные C # и JavaScript, поэтому этот ответ не является законным.
Показать ещё 8 комментариев
30

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

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!');
}
  • 0
    Вы говорите хак, но я почти уверен, что это случай использования генераторов ... :) (хорошо, за исключением того, что это зависит от this является ли это объектом окна)
28

На самом деле ответ на первую часть вопроса "Да" на каждом языке программирования. Например, это относится к 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;
}
  • 27
    Я не думаю, что это возможно на каждом языке программирования. Например, не все языки имеют препроцессоры. В связи с этим не все языки используют && для логических «и».
  • 3
    Я нашел способ, который работает как в Python, так и в C ++, который использует перегрузку операторов.
Показать ещё 17 комментариев
27

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

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

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

  • 4
    Работает и во второй раз!
26

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

var a = new Proxy({ i: 0 }, {
    get: (target, name) => name === Symbol.toPrimitive ? () => ++target.i : target[name],
});
console.log(a == 1 && a == 2 && a == 3);

Proxies в основном претендуют на то, чтобы быть целевым объектом (первым параметром), но перехватывают операции над целевым объектом (в этом случае операция get get), так что есть возможность сделать что-то другое, кроме поведения объекта по умолчанию, В этом случае действие "get property" вызывается на a, когда == заставляет его тип, чтобы сравнить его с каждым числом. Это происходит:

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

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

  • 0
    Интересное использование прокси
  • 2
    Однако в этом нет смысла использовать прокси-сервер - определение Symbol.toPrimitive таким же образом для объекта будет работать так же хорошо.
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.

21

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

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

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

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

11

Этот использует свойство 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), нет необходимости глобального.
  • 12
    @NinaScholz конечно, но мы говорим о плохой практике здесь - просто позвольте мне иметь это: D
2

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

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 не вызываются.

0

Это хороший вопрос. Я также хотел добавить реализацию 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");
        }
    }
}
0

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

-2

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню