Pode (a == 1 && a == 2 && a == 3) avaliar como verdadeiro?

2484

Nota do moderador: resista ao desejo de editar o código ou remover este aviso. O padrão de espaço em branco pode fazer parte da pergunta e, portanto, não deve ser adulterado desnecessariamente. Se você estiver no campo "espaço em branco é insignificante", poderá aceitar o código como está.

É possível (a== 1 && a ==2 && a==3)avaliar trueem JavaScript?

Esta é uma pergunta de entrevista feita por uma grande empresa de tecnologia. Isso aconteceu duas semanas atrás, mas ainda estou tentando encontrar a resposta. Sei que nunca escrevemos esse código em nosso trabalho cotidiano, mas estou curioso.

Dimpu Aravind Buddha
fonte
9
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
deceze
109
Para as pessoas que aparentemente votaram para esconder isso como muito amplo : isso é uma escavação no Javascript, dizendo que há muitas respostas válidas?
tomsmeding
24
Algumas pessoas ficam filosofando sobre o que é possível. Outros concentram seus esforços em criar ou não produtos viáveis ​​e corretos para seus clientes. Na IMO, essa pergunta não tem utilidade prática além do fato de que você nunca deve fazer esse tipo de pergunta em uma entrevista ou escrever esse tipo de código. É por isso que deve ser fechado. Quero dizer, realmente, a empresa percebe que pagou a alguém dinheiro real para sentar e conversar sobre essas coisas?
usar o seguinte código
15
Depois de ler as respostas, a moral da história é: não use ==quando você quer dizer ===, tenha um padrão de codificação que proíba nomes de variáveis ​​não ASCII e tenha um processo de vinculação que imponha as duas morais anteriores.
Jesse C. Slicer
87
Nota do moderador: O Stack Overflow tem um histórico de pessoas concordando com respostas em diferentes idiomas para o que está em questão. Essas são tentativas de responder à pergunta porque são soluções para o problema geral, embora em um idioma diferente. Evite sinalizá-los como "não uma resposta". Dito isso, evite postar mais respostas em diferentes idiomas - há um motivo pelo qual essa pergunta é específica ao JavaScript, conforme apontado pelos comentários em algumas dessas outras respostas, e há um motivo pelo qual gostamos de nossas perguntas específicas ao idioma permanecer assim.
BoltClock

Respostas:

3323

Se você tirar proveito de como ==funciona , você pode simplesmente criar um objeto com uma função personalizada toString(ou valueOf) que altera o que ele retorna cada vez que é usado, de modo a satisfazer todas as três condições.

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

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


A razão pela qual isso funciona é devido ao uso do operador de igualdade frouxa. Ao usar igualdade solta, se um dos operandos for de um tipo diferente do outro, o mecanismo tentará converter um para o outro. No caso de um objeto à esquerda e um número à direita, ele tentará converter o objeto em um número ligando primeiro, valueOfse for possível chamar , e, na sua falta, ligará toString. Eu usei toStringneste caso simplesmente porque é o que me veio à mente, valueOffaria mais sentido. Se eu retornasse uma string de toString, o mecanismo tentaria converter a string em um número, fornecendo o mesmo resultado final, embora com um caminho um pouco mais longo.

Kevin B
fonte
70
Você poderia conseguir isso alterando a valueOf()operação implícita ?
Sterling Archer
43
Sim, valueOf funciona no lugar de toString pelo mesmo motivo
Kevin B
4
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
deceze
13
De acordo com isso, uma conversão numérica será tentada primeiro, portanto valueOfé um pouco melhor.
Salman A
6
@Pureferret O lado esquerdo da comparação de igualdade é um objeto, não um número. O fato de esse objeto ter uma propriedade numérica inão incomoda o mecanismo. ;)
tomsmeding 16/01
2057

Não pude resistir - as outras respostas são indubitavelmente verdadeiras, mas você realmente não pode passar pelo seguinte código:

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

Observe o espaçamento estranho na ifdeclaração (que copiei da sua pergunta). É o Hangul de meia largura (que é coreano para quem não conhece) que é um caractere de espaço Unicode que não é interpretado pelo script ECMA como um caractere de espaço - isso significa que é um caractere válido para um identificador. Portanto, existem três variáveis ​​completamente diferentes, uma com o Hangul após a, uma com ele antes e a última com apenas a. Substituindo o espaço _por legibilidade, o mesmo código ficaria assim:

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

Confira a validação no validador de nome de variável do Mathias . Se esse espaçamento estranho foi realmente incluído na pergunta, tenho certeza de que é uma dica para esse tipo de resposta.

Não faça isso. A sério.

Edit: Chegou ao meu conhecimento que (embora não seja autorizado a iniciar uma variável), o marceneiro de largura zero e zero-width não marcenaria personagens também são permitidos em nomes de variáveis - ver ofuscando JavaScript com de largura zero personagens - prós e contras ? .

Seria o seguinte:

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

Jeff
fonte
368
A julgar pelo espaçamento ímpar da pergunta original, acho que essa é EXATAMENTE a resposta que a pergunta da entrevista estava procurando - explorando caracteres não espaciais que parecem espaços. Bom lugar!
Baracus
18
@ Baracus Foi RonJohn quem notou o espaçamento estranho em seu comentário na resposta de Kevin que me lembrou essa técnica (terrível), então não posso me responsabilizar por detectá-la. Fiquei meio surpreso que ninguém já tivesse respondido com isso, pois foi ao redor do meu trabalho alguns anos atrás por causa de uma postagem em algum lugar - eu meio que assumi que já era um conhecimento comum.
Jeff Jeff
102
Obviamente, isso é proibido como uma brecha padrão , que também se aplica a entrevistas. [citação necessário]
Sanchises 16/01
13
Considerando o espaçamento original, pode ser ainda pior, ou seja, uma variável var ᅠ2 = 3foi usada; então existem as três variáveis aᅠᅠ= 1, ᅠ2 = 3, a = 3( a␣ = 1, ␣2 = 3, a = 3, para que (a␣==1 && a==␣2 && a==3)) ...
Holger
2
@ AL-zami, existe um caractere extra em duas das variáveis, que aparece na tela como um espaço, mas é interpretado como parte do identificador, o que significa que existem três variáveis ​​separadas - a, a e a - o caractere extra é o espaço de meia largura Hangul.
Jeff Jeff
620

É POSSÍVEL!

var i = 0;

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

Isso usa um getter dentro de uma withinstrução para permitir aavaliar três valores diferentes.

... isso ainda não significa que isso deva ser usado em código real ...

Pior ainda, esse truque também funcionará com o uso de ===.

  var i = 0;

  with({
    get a() {
      return ++i;
    }
  }) {
    if (a !== a)
      console.log("yep, this is printed.");
  }

Jonas Wilms
fonte
65
Sim, eu estava tentando a mesma coisa :) Portanto, a resposta correta na entrevista seria: "Isso não pode acontecer no meu código, porque eu nunca uso with".
Pointy
7
@ Pointy - E, eu programa em modo estrito, onde withnão é permitido.
precisa saber é o seguinte
6
@Pointy na resposta aceita que fazer algo semelhante, sem o withque ele possa acontecer
Jungkook
2
@ jorrit que ninguém usaria ==. E ===impede a resposta aceita
Jonas Wilms
4
@JonasW. Muitas pessoas ainda usam, ==mas eu não as vejo withdesde ... bem, na verdade nunca fora da documentação do JS, onde diz "por favor, não use isso". Enfim, uma boa solução.
Wortwart
516

Exemplo sem getters ou valueOf:

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

Isso funciona porque ==chama toStringquais chamadas .joinpara matrizes.

Outra solução, usando o Symbol.toPrimitiveque é um equivalente ES6 de toString/valueOf:

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

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

georg
fonte
9
without valueOf, bem ... é mais indireto, mas basicamente a mesma coisa.
Jonas Wilms
11
Eu realmente gosto dessa solução porque você não substitui nada, exceto a função de junção dos objetos, e é apenas um hack muito limpo e fácil de ler que faz a lógica avaliar como verdadeira.
Alex Pedersen
28
Honestamente, acho que essa é a melhor resposta. Não envolve nada fora do comum, apenas definindo alguns valores. Muito fácil de entender, mesmo com o conhecimento básico de JS. Bem feito.
Zac Delventhal
14
Isso faz tanto sentido que quase parece útil.
Andrew Andrew
7
Eu sabia que a maioria das respostas seria sobre abuso toStringou valueOfmas essa me pegou completamente desprevenido. Muito esperto e eu não sabia que ele ligava .joininternamente, mas faz total sentido.
precisa saber é o seguinte
268

Se for perguntado se é possível (não DEVE), ele pode pedir a "a" para retornar um número aleatório. Seria verdade se gerar 1, 2 e 3 sequencialmente.

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
fonte
102
Eu daria deliberadamente essa resposta mesmo se soubesse as outras soluções, porque ela responde à pergunta, mas obviamente não é o que elas estavam procurando. Jogue jogos estúpidos, ganhe prêmios estúpidos.
ESR
2
Mas e se forem necessárias mais de 1000 tentativas?
Piyin
9
@Piyin Se forem necessárias mais de 1000 tentativas, você ganha um prêmio!
Skeets
5
Eu gosto dessa resposta porque levá-la ao extremo sugere que isso é possível em qualquer idioma se os registros / cache da CPU forem atingidos com raios cósmicos suficientes enquanto o programa estiver em execução ou se alguém deliberadamente executa uma falha de energia de modo que o ramo de falha do o if condicional na verdade não salta.
Ponkadoodle
Mais baixo: 1, mais alto: 412.
KyleFairns 30/01/19
210

Quando você não pode fazer nada sem expressões regulares:

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

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

Funciona devido ao valueOfmétodo personalizado chamado quando Object comparado com o primitivo (como Number). O truque principal é que a.valueOfretorna um novo valor toda vez porque está chamando execa expressão regular com gsinalizador, o que causa a atualização lastIndexdessa expressão regular toda vez que a correspondência é encontrada. Então, pela primeira vez this.r.lastIndex == 0, ele corresponde 1e atualizações lastIndex: this.r.lastIndex == 1, tão próxima regex tempo irá corresponder 2e assim por diante.

Kos
fonte
22
@ Abdillah um objeto regex lembrará o último índice correspondente, a chamada execnovamente começará a pesquisar nesse índice. MDN não é muito claro.
Simon Chan
Entendo, então o this.robjeto regex lembra o estado / índice. Obrigado!
Abdillah
Eu recomendaria passar uma string para execporém, não um número inteiro a ser stringified.
Bergi 18/01/19
usar regex e agora você tem dois problemas
Aleksey Solovey
191

Isso pode ser realizado usando o seguinte no escopo global. Para nodejsuso em globalvez de windowno código abaixo.

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

Esta resposta abusa das variáveis ​​implícitas fornecidas pelo escopo global no contexto de execução, definindo um getter para recuperar a variável.

Jontro
fonte
Isso pressupõe que aé uma propriedade da thisqual não parece ser. Se afosse uma variável local (que parece), isso não funcionaria.
precisa saber é o seguinte
1
@ jfriend00 você quer dizer se você colocou var a; algum lugar?
jontro
Sim. Referenciar a == 1implica que aé uma variável em algum lugar, não uma propriedade de this. Embora exista um lugar excêntrico como globais em que ambos possam ser verdadeiros, geralmente declarar uma variável com var aou let asignifica que não existe nenhum thisque permita acessar acomo uma propriedade que o código assume. Então, seu código aparentemente está assumindo algo estranho de variável global. Por exemplo, seu código não funciona no node.js e não no modo estrito dentro de uma função. Você deve especificar as circunstâncias exatas em que funciona e provavelmente explicar por que funciona. Caso contrário, é enganoso.
jfriend00
@ jfriend00 bem certo. Não tenho certeza de que agregaria muito mais valor em combinação com as outras respostas já. Irá atualizar a resposta
jontro 15/01/18
14
A questão era: poderia esse "sempre" ser verdade. E a resposta é sim, e este é um dos cenários em que pode ser verdade: anão é uma variável local e é definida no escopo global com um getter crescente.
Zac Delventhal
190

Isso é possível no caso de a variável aser acessada por, digamos, 2 trabalhadores da Web por meio de um SharedArrayBuffer, além de algum script principal. A possibilidade é baixa, mas é possível que, quando o código é compilado para código de máquina, os trabalhadores web atualizar a variável aapenas no tempo para que as condições a==1, a==2e a==3estão satisfeitos.

Este pode ser um exemplo de condição de corrida em ambiente multithread fornecido por trabalhadores da Web e SharedArrayBuffer em JavaScript.

Aqui está a implementação básica acima:

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

No meu MacBook Air, isso ocorre após cerca de 10 bilhões de iterações na primeira tentativa:

insira a descrição da imagem aqui

Segunda tentativa:

insira a descrição da imagem aqui

Como eu disse, as chances serão baixas, mas, com tempo suficiente, ela atingirá a condição.

Dica: Se demorar muito no seu sistema. Tente apenas a == 1 && a == 2e mude Math.random()*3para Math.random()*2. Adicionar mais e mais à lista diminui a chance de acertar.

mehulmpt
fonte
50
Honestamente, esta é a melhor resposta. Todas as outras respostas requerem uma tentativa deliberada de fazer algo profundamente pouco intuitivo. Essa resposta realmente reflete algo que pode acontecer no mundo real - uma condição de corrida.
Tom Swirly
34
Não apenas isso - eu realmente vi isso acontecer no mundo real. Não com a condição exata na pergunta, mas certamente com a verificação (a == 1) no início de uma função e (a == 2) posteriormente na função, e com o código atingindo ambas as condições. Para sua informação, a primeira vez que vi isso acontecer foi em um controlador de motor de carro e implementamos os padrões de codificação. A segunda vez foi em um sistema de dispensador de palha e flare para aeronaves militares e, no meu primeiro dia na empresa , encontrei e consertei isso, enquanto o restante da equipe ainda discutia o problema. (Kudos nível: alta :)!
Graham
38
Então, você trabalhou em "controladores de motores de carros" e "sistemas de dispensadores de chaff e flare", programados em javascript com profissionais da web? Acho que não vou sair de novo.
psaxton
12
@psaxton :) Claro que não - mas temos software multiencadeado com dados compartilhados. Esse é um antipadrão para todos os softwares multithread, não específico para Javascript ou para trabalhadores da Web. Não importa se você está programando em linguagem assembly, Brainf * ck, Visual BASIC, C ou Javascript - se você fizer isso com dados compartilhados em um aplicativo multithread, ele sempre falhará.
Graham
4
Penso que este é agora um invólucro elaborado em torno da resposta do @ jontro.
Qntm 23/0118
148

Isso também é possível usando uma série de getters de sobregravação automática:

(Isso é semelhante à solução de jontro, mas não requer uma variável de contador.)

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

Patrick Dark
fonte
61
Observe que a abordagem de usar um getter também funciona ===, não apenas ==.
Makyen 16/01
Essa solução depende de thisser o objeto global dentro do corpo da função de seta.
Roy Tinker
@ Midnightas Eu não categorizaria nenhuma outra resposta como "código de pirâmide" .
Patrick Roberts
Observe que isso também funciona com ordem arbitrária, não é? Como (a == 3 && a == 2 && a == 1)?
Johannes
131

Como alternativa, você pode usar uma classe para isso e uma instância para a verificação.

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

var a = new A;

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

EDITAR

Usando classes ES6, ficaria assim

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

Nina Scholz
fonte
5
apenas function A() {value = 0;no começo?
Dave C
valueOfestá sendo substituído, this method is usually called automatically by JavaScript behind the scenes, and not explicitly in codepor isso, quando se compara o valor que ele realmente incrementa a ..
Danyal Sandeelo
130

Eu não vejo essa resposta já postada, então eu vou jogar essa também. Isso é semelhante à resposta de Jeff com o espaço Hangul de meia largura.

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

Você pode notar uma ligeira discrepância com o segundo, mas o primeiro e o terceiro são idênticos a olho nu. Todos os três são caracteres distintos:

a- Letra minúscula latina A
- Letra minúscula latina A completa
а - Letras minúsculas cirílicas A

O termo genérico para isso é "homoglifos": caracteres unicode diferentes que têm a mesma aparência. Normalmente é difícil conseguir três totalmente indistinguíveis, mas em alguns casos você pode ter sorte. A, Α, А, e Ꭺ iria funcionar melhor (Latin-A, alfa grego , cirílico-A , e Cherokee-A , respectivamente; infelizmente, o grego e Cherokee letras minúsculas são muito diferentes do latim a: α, e assim doesn ajuda com o trecho acima).

Existe toda uma classe de ataques de homoglyph por aí, mais comumente em nomes de domínio falsos (por exemplo, wikipediа.org(cirílico) vs wikipedia.org(latino)), mas também pode aparecer no código; normalmente referidos como sendo dissimulados (como mencionado em um comentário, perguntas [dissimuladas] agora estão fora de tópico no PPCG , mas costumavam ser um tipo de desafio em que esses tipos de coisas apareciam). Usei este site para encontrar os homoglifos usados ​​para esta resposta.

Draco18s não confia mais no SE
fonte
19
"Pequena discrepância" não é como eu chamaria isso.
4
@hvd Depende totalmente da renderização da fonte. Isto é o que eu vejo .
Draco18s não confia mais em SE
1
@ Jake Sim, a minúscula latina A de largura total A não é o maior homóglifo (mas as variantes de letras maiúsculas são incríveis). Geralmente, você precisa apenas de dois para obter o efeito desejado.
Draco18s não confia mais em
@ Draco18s concordou re: apenas 2 geralmente são necessários. Bom trabalho com informações extras também!
JakeSteam
10
Você também pode usar o seletor de variantes unicode (U + FE00..U + FE0F). Nenhum destes são a: a︀ a︁ a︂. Não precisa mais se preocupar com discrepâncias.
Salman A
108

Sim, é possível! 😎

»JavaScript

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

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

O código acima é uma versão curta (obrigado a @Forivin por sua observação nos comentários) e o código a seguir é original:

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

Se você apenas vê o lado superior do meu código e o executa, diz WOW, como?

Então eu acho que basta dizer Sim, é possível para alguém que te disse: Nada é impossível

Truque: usei um caractere oculto depois ifpara criar uma função cujo nome é semelhante if. No JavaScript, não podemos substituir as palavras-chave, então eu forcei a usar dessa maneira. É uma farsa if, mas funciona para você neste caso!


» C #

Também escrevi uma versão em C # ( com o aumento da propriedade value technic ):

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

Demonstração ao vivo

RAM
fonte
56
A versão javascript é um verdadeiro crime contra a humanidade e a capacidade de fazer isso deve ser ilegal pelas convenções da ONU. Acho que já é hora de limparmos o mundo de todo o conhecimento sobre javacript.
Clearer
2
A declaração da função pode ser ainda mais curta. if‌=()=>!0
Forivin
4
Por que diabos você usou document.write? Essa é uma maneira infalível de não ser contratado, independentemente do restante da resposta.
Cerbrus
3
@ Cerbrus, obrigado pela sua nota. Escrevi primeiro minha resposta, console.logmas mudei para document.write. Realmente sempre uso console.lognos meus códigos, mas aqui só quero mostrar um texto aos usuários na caixa de trechos de código StackOverflow. Então, eu queria mostrar minha mensagem mais bonita do que a mensagem gerada por console.log. Clique no Run Code Snippetbotão na minha resposta e em outras respostas. O SO Code Snippet me permitiu usar html e JS e CSS, então eu queria usá-lo na minha resposta e torná-lo agradável. Eu acho que não tem nenhum efeito colateral negativo e não fez minha resposta grande ou completa.
RAM
1
@Clearer, se as convenções da ONU pudessem mudar o mundo de maneira eficaz, então teríamos um mundo melhor do que isso. Precisamos de algumas coisas mais de declaração na ONU e, até aquele dia, acho que podemos usar esse truque Javascript meu;)
RAM
97

Javascript

a == a +1

Em JavaScript, não há números inteiros, mas apenasNumber s, que são implementados como números de ponto flutuante de precisão dupla.

Isso significa que, se um número afor grande o suficiente, ele poderá ser considerado igual a três números inteiros consecutivos:

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

É verdade que não é exatamente o que o entrevistador pediu (não funciona com a=0 ), mas não envolve nenhum truque com funções ocultas ou sobrecarga do operador.

Outras línguas

Para referência, existem a==1 && a==2 && a==3soluções em Ruby e Python. Com uma pequena modificação, também é possível em Java.

Rubi

Com um costume ==:

class A
  def ==(o)
    true
  end
end

a = A.new

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Ou um aumento a:

def a
  @a ||= 0
  @a += 1
end

if a == 1 && a == 2 && a == 3
  puts "Don't do this!"
end

Pitão

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

É possível modificar o Integercache 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.");
        }
    }
}
Eric Duminil
fonte
27
@ cᴏʟᴅsᴘᴇᴇᴅ: Java, Javascript, JavaScript, JavaScript :) Já existem respostas JS suficientemente boas. Eu apenas pensei que seria interessante mostrar como isso pode ser feito em outros idiomas e possivelmente dar algumas idéias aos desenvolvedores de JS.
Eric Duminil
2
@ cᴏʟᴅsᴘᴇᴇᴅ: atualizado com um exemplo de JS.
Eric Duminil
1
Por que a versão Java não funciona Integer a = 42(ou funciona)? Pelo que entendi, a Integer a = 42; a == 1 && a == 2 && a == 3caixa automática deve conter todas as entradas. Ou isso desmarca um para as comparações?
CAD18
@ CAD97: Integer == intparece resultar em unboxing. Mas usar Integer#equals(int)forças para autoboxing, para que funcione. Obrigado pelo comentário!
Eric Duminil
@StephanBijzitter: Por favor, explique. Até onde eu sei, existem apenas Numbersno JS, que são basicamente como doubles. Eles podem parecer números inteiros e você pode usá-los como números inteiros, mas ainda não são números inteiros. Eu não acho que n == n + 1nunca pode ser verdade para inteiros em Java / Python / C / Ruby / ...
Eric Duminil
80

Esta é uma versão invertida do @ de Jeff resposta * onde um personagem oculto (U + 115F, U + 1160 ou U + 3164) é usado para criar variáveis que olhar como 1, 2e 3.

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

* Essa resposta pode ser simplificada usando o marceneiro de largura zero (U + 200C) e o marceneiro de largura zero (U + 200D). Ambos os caracteres são permitidos dentro de identificadores, mas não no começo:

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);
****/

Outros truques são possíveis usando a mesma idéia, por exemplo, usando seletores de variação Unicode para criar variáveis ​​que se parecem exatamente ( a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

Salman A
fonte
75

Regra número um das entrevistas; nunca diga impossível.

Não há necessidade de truques de caracteres ocultos.

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
fonte
6
Ai. __defineGetter__na verdade não faz parte da linguagem js, apenas uma versão feia do defineProperty. typeofnão é uma função e isso não declarado ié simplesmente horrível. Ainda parece valer 40 votos positivos: /
Jonas Wilms 17/01
6
@JonasW. 41 upvotes :-) Estou ciente de que isso __defineGetter__foi preterido por developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…, mas ele é executado claramente no meu FireFox v 57.0.4, então optei por mostrar isso em vez de defineProperty()porque o código legado é real e não pode ser ignorado. Independentemente da feiúra, declarar ida maneira que eu fiz é um comportamento bem conhecido / documentado. Talvez eu era apenas em um clima PCG ¯ \ _ (ツ) _ / ¯
MonkeyZeus
68

Honestamente, se existe uma maneira de avaliar se é verdade ou não (e, como outros demonstraram, existem várias maneiras), a resposta que eu procuraria, falando como alguém que conduziu centenas de entrevistas, seria algo ao longo das linhas de:

"Bem, talvez sim, em um conjunto estranho de circunstâncias que não são imediatamente óbvias para mim ... mas se eu encontrasse isso em código real, usaria técnicas comuns de depuração para descobrir como e por que estava fazendo o que estava fazendo e refatorar imediatamente o código para evitar essa situação ... mas mais importante: eu NUNCA escreveria absolutamente esse código em primeiro lugar, porque essa é a própria definição de código complicado, e me esforço para nunca escrever código complicado ".

Eu acho que alguns entrevistadores se ofenderiam com o fato de ser obviamente uma pergunta muito complicada, mas eu não me importo com desenvolvedores que tenham uma opinião, especialmente quando eles podem apoiá-la com um pensamento fundamentado e podem encaixar minha pergunta em uma declaração significativa sobre si mesmos.

Frank W. Zammetti
fonte
13
A questão (ou todas as perguntas da entrevista) é provavelmente testar a disposição dos candidatos de pensar em um problema, especialmente aqueles que são "aparentemente óbvios", como este. Alguém que se recusa a pensar porque acredita que "sabe" a resposta não é um bom contratado.
Shammoo 17/01/19
5
@Don Hatch Não, eu não os puniria se eles respondessem de boa fé e, especialmente, se eles dessem uma resposta correta, como os outros mostraram ... mas eu pediria um acompanhamento para tentar descobrir se eles acham que é uma boa maneira de escrever código ou não. Conhecer e ser capaz de encontrar uma resposta "correta" é apenas parte de ser um bom desenvolvedor. Muito mais importante para um desenvolvedor "profissional" é escrever um código compreensível e sustentável no caminho, muitas vezes por desenvolvedores menos capazes. Desenvolvedores excessivamente inteligentes são tão ruins quanto os IME incapazes.
precisa saber é o seguinte
16
Isso não responde à pergunta.
precisa saber é o seguinte
6
O triste dessa resposta é que um usuário de 1rep respondeu ontem e recebeu 2 votos negativos, o que excluiu esta pergunta.
Jonas Wilms
8
@JohnColeman, a pergunta pergunta como o código pode ser avaliado como verdadeiro. Ele não pergunta os motivos pelos quais o entrevistador propôs a pergunta em primeiro lugar. Essa resposta nem tenta abordar a pergunta que está sendo feita e, em vez disso, concentra-se inteiramente na versão "o que eu faria" de uma tentativa de adivinhar qual era o objetivo do entrevistador. Se essa fosse a pergunta, seria muito ampla. Portanto, esta resposta não pertence aqui ou em qualquer lugar do site.
precisa saber é o seguinte
43

Se você receber uma pergunta dessa entrevista (ou perceber algum comportamento igualmente inesperado em seu código), pense sobre que tipo de coisas poderia causar um comportamento que parece impossível à primeira vista:

  1. Codificação : neste caso, a variável que você está vendo não é a que você pensa que é. Isso pode acontecer se você intencionalmente mexer com Unicode usando homóglifos ou caracteres de espaço para fazer o nome de uma variável parecer com outra, mas problemas de codificação também podem ser introduzidos acidentalmente, por exemplo, ao copiar e colar código da Web que contém código Unicode inesperado pontos (por exemplo, porque um sistema de gerenciamento de conteúdo fez algumas "formatações automáticas", como substituir flpor Unicode 'LATIN SMALL LIGATURE FL' (U + FB02)).

  2. Condições de corrida : Pode ocorrer uma condição de corrida , ou seja, uma situação em que o código não está sendo executado na sequência esperada pelo desenvolvedor. As condições de corrida geralmente acontecem no código com vários threads, mas vários threads não são um requisito para que as condições de corrida sejam possíveis - a assincronicidade é suficiente (e não se confunda, a assíncrona não significa que vários threads sejam usados ​​sob o capô ).

    Observe que, portanto, o JavaScript também não está livre de condições de corrida apenas porque é de thread único. Veja aqui um exemplo simples de thread único - mas assíncrono -. No contexto de uma única declaração, a condição de corrida, no entanto, seria bastante difícil de encontrar em JavaScript.

    O JavaScript com trabalhadores da Web é um pouco diferente, pois você pode ter vários threads. O @mehulmpt nos mostrou uma ótima prova de conceito usando trabalhadores da web .

  3. Efeitos colaterais : um efeito colateral da operação de comparação de igualdade (que não precisa ser tão óbvia como nos exemplos aqui, geralmente os efeitos colaterais são muito sutis).

Esse tipo de problema pode aparecer em muitas linguagens de programação, não apenas no JavaScript, por isso não estamos vendo um dos WTFs clássicos do JavaScript aqui 1 .

Obviamente, a pergunta da entrevista e as amostras aqui parecem muito bem-sucedidas. Mas eles são um bom lembrete de que:

  • Os efeitos colaterais podem ficar realmente desagradáveis ​​e que um programa bem projetado deve estar livre de efeitos colaterais indesejados.
  • O estado multithreading e mutável pode ser problemático.
  • Não executar corretamente a codificação de caracteres e o processamento de strings pode levar a erros desagradáveis.

1 Por exemplo, você pode encontrar um exemplo em uma linguagem de programação totalmente diferente (C #) exibindo um efeito colateral (óbvio) aqui .

Dirk Vollmar
fonte
1
Então, a questão se torna muito ampla. Diferentes idiomas podem implementar isso com vários graus de facilidade. A questão ganhou muita força porque é um Q&A específico de JS, mas esse é apenas o meu 2c.
cs95
1
as causas são c # e javascript diferentes, portanto, essa resposta não é legítima.
Edwin
3
@ Edwin: As causas são exatamente as mesmas: Unicode mexendo com glifos ou caracteres espaciais de aparência semelhante, condições de corrida ou efeitos colaterais da operação de comparação (a última sendo mostrada no meu exemplo).
quer
2
@ cᴏʟᴅsᴘᴇᴇᴅ: Às vezes, olhar as coisas de um ângulo mais amplo ajuda a ver o problema real.
Dirk Vollmar
3
Eu gostaria que esta resposta pudesse ser marcada para esta pergunta de alguma maneira "meta". Depois de ler todas as respostas acima, eu estava à esquerda sentir como JS tem assim muitos buracos, mas você simplesmente resumiu todas as respostas de uma só vez. E você fez isso de uma maneira que transforma isso em uma pergunta de entrevista estelar (se a tag específica de idioma for removida) na minha opinião. Bravo!
KCE 22/01
41

Aqui está outra variação, usando uma matriz para exibir os valores desejados.

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

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

Théophile
fonte
31

Ok, outro hack com geradores:

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
fonte
Você diz truque, mas eu tenho certeza que este é o caso de uso de geradores de ... :) (bem, exceto que este se baseia em thisser o objeto de janela)
Cody G
29

Usando Proxies :

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

Os proxies basicamente fingem ser um objeto de destino (o primeiro parâmetro), mas interceptam operações no objeto de destino (neste caso, a operação "get property") para que haja uma oportunidade de fazer algo diferente do comportamento padrão do objeto. Nesse caso, a ação "obter propriedade" é chamada aquando ==coagida seu tipo para compará-lo a cada número. Isto acontece:

  1. Criamos um objeto de destino { i: 0 }, onde a ipropriedade é nosso contador
  2. Criamos um proxy para o objeto de destino e o atribuímos a a
  3. Para cada a ==comparação, ao tipo de é coagido a um valor primitivo
  4. Esse tipo de coerção resulta em chamar a[Symbol.toPrimitive]()internamente
  5. O Proxy intercepta a obtenção da a[Symbol.toPrimitive]função usando o "get handler"
  6. Os controlos do proxy "obter manipulador" que a propriedade que está sendo obtido é Symbol.toPrimitive, caso em que ele é incrementado e, em seguida, retorna o contador do objeto de destino: ++target.i. Se uma propriedade diferente estiver sendo recuperada, voltaremos a retornar o valor padrão da propriedade,target[name]

Assim:

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

Como na maioria das outras respostas, isso funciona apenas com uma verificação de igualdade flexível ( ==), porque verificações estritas de igualdade ( ===) não fazem coerção de tipo que o Proxy possa interceptar.

IceCreamYou
fonte
2
Não há sentido em usar um proxy para isso - definir Symbol.toPrimitiveda mesma maneira em um objeto também funcionaria.
Ry-
27

Na verdade, a resposta para a primeira parte da pergunta é "Sim" em todas as linguagens de programação. Por exemplo, isso é no caso de 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;
}
Gustavo Rodríguez
fonte
27
Não acho que seja possível em todas as linguagens de programação. Nem todos os idiomas têm pré-processadores, por exemplo. Por falar nisso, nem todos os idiomas usam &&para "e" lógico.
Keith Thompson
3
Eu encontrei uma maneira que funciona tanto em Python quanto em C ++ que usa sobrecarga de operador.
Donald Duck
7
E você pode fazer isso em Java usando reflexão e bagunçando o cache inteiro.
CAD97
7
Não é possível fazê-lo em idiomas que não suportam mutação nesse local, por exemplo, nada comparável está disponível no haskell #
Jason Carr
4
A questão é perguntar sobre JavaScript, não C ++.
Todos os trabalhadores têm Essencial
26

O mesmo, mas diferente, mas ainda o mesmo (pode ser "testado" várias vezes):

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

Minha ideia partiu de como a equação do tipo de objeto Number funciona.

Preda7or
fonte
4
Funciona pela segunda vez também!
Salman A
25

Uma resposta do ECMAScript 6 que utiliza símbolos:

const a = {value: 1};
a[Symbol.toPrimitive] = function() { return this.value++ };
console.log((a == 1 && a == 2 && a == 3));

Devido ao ==uso, JavaScript é suposto coerce aem algo perto do segundo operando ( 1, 2, 3neste caso). Porém, antes que o JavaScript tente descobrir por si só, ele tenta chamar Symbol.toPrimitive. Se você fornecer Symbol.toPrimitiveJavaScript, usará o valor que sua função retornará. Caso contrário, o JavaScript chamaria valueOf.

Omar Alshaker
fonte
24

Eu acho que este é o código mínimo para implementá-lo:

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

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

Criando um objeto fictício com um costume valueOfque incrementa uma variável global iem cada chamada. 23 caracteres!

gafi
fonte
14

Este usa o defineProperty com um bom efeito colateral causando variável global!

var _a = 1

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

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

Ben Aubin
fonte
8
você pode usar um encerramento a: get: (a => () => ++a)(0),não é necessário global.
Nina Scholz
13
@NinaScholz, com certeza, mas estamos falando de más práticas aqui - deixe-me saber o seguinte: D
Ben Aubin
1

Substituindo valueOfem uma declaração de classe, isso pode ser feito:

class Thing {
    constructor() {
        this.value = 1;
    }

    valueOf() {
        return this.value++;
    }
}

const a = new Thing();

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

O que acontece é que valueOfé chamado em cada operador de comparação. No primeiro, aserá igual 1, no segundo, aserá igual 2, e assim por diante, porque cada vez que valueOfé chamado, o valor de aé incrementado.

Portanto, o console.log será acionado e emitido (no meu terminal de qualquer maneira) Thing: { value: 4}, indicando que o condicional era verdadeiro.

Jonathan Kuhl
fonte