it-swarm.dev

Può (a == 1 && a == 2 && a == 3) mai valutare per vero?

Nota del moderatore: Si prega di resistere alla tentazione di modificare il codice o rimuovere questo avviso. Il modello di spazio bianco può essere parte della domanda e pertanto non dovrebbe essere manomesso inutilmente. Se ti trovi nel campo "whitespace is insignificant", dovresti essere in grado di accettare il codice così com'è.

È mai possibile che (a== 1 && a ==2 && a==3) possa valutare true in JavaScript?

Questa è una domanda di intervista fatta da un'importante azienda tecnologica. È successo due settimane fa, ma sto ancora cercando di trovare la risposta. So che non scriviamo mai un codice del genere nel nostro lavoro quotidiano, ma sono curioso.

2331

Se sfrutti come == works , puoi semplicemente creare un oggetto con una funzione toString (o valueOf) personalizzata che cambia ciò che restituisce ogni volta che viene utilizzata in modo tale da soddisfare tutte e tre le condizioni.

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

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


Il motivo per cui funziona è dovuto all'uso dell'operatore di uguaglianza libera. Quando si utilizza l'uguaglianza libera, se uno degli operandi è di un tipo diverso rispetto all'altro, il motore tenterà di convertire uno all'altro. Nel caso di un oggetto a sinistra e un numero a destra, tenterà di convertire l'oggetto in un numero chiamando prima valueOf se è callable e, in caso contrario, chiamerà toString. Ho usato toString in questo caso semplicemente perché è quello che mi è venuto in mente, valueOf avrebbe avuto più senso. Se invece restituissi una stringa da toString, il motore avrebbe quindi tentato di convertire la stringa in un numero dandoci lo stesso risultato finale, sebbene con un percorso leggermente più lungo.

3185
Kevin B

Non ho potuto resistere - le altre risposte sono indubbiamente vere, ma non si può davvero superare il seguente codice:

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

Notare la strana spaziatura nella dichiarazione if (che ho copiato dalla tua domanda). È l'Hangul a mezza larghezza (che è coreano per chi non lo conosce) che è un carattere di spazio Unicode che non è interpretato dallo script ECMA come carattere di spazio - ciò significa che è un carattere valido per un identificatore. Quindi ci sono tre variabili completamente diverse, una con l'Hangul dopo l'a, una con quella prima e l'ultima con solo a. Sostituendo lo spazio con _ per la leggibilità, lo stesso codice sarebbe simile a questo:

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

Controlla la convalida sul validatore del nome variabile di Mathias . Se quella strana spaziatura fosse effettivamente inclusa nella loro domanda, sono certo che sia un suggerimento per questo tipo di risposta.

Non farlo Sul serio.

Edit: Mi è venuto in mente che (anche se non è permesso avviare una variabile) il Zero-width joiner e Zero-width non-joiner caratteri sono permessi anche nei nomi delle variabili - vedere Obfuscating JavaScript con caratteri a larghezza zero - pro e contro? .

Questo apparirebbe come il seguente:

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

1969
Jeff

IT IS POSSIBILE!

var i = 0;

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

Questo utilizza un getter all'interno di un'istruzione with per consentire a a di valutare tre valori diversi.

... questo ancora non significa che dovrebbe essere usato nel codice reale ...

Ancora peggio, questo trucco funzionerà anche con l'uso di ===.

  var i = 0;

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

591
Jonas Wilms

Esempio senza getter o valueOf:

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

Ciò funziona perché == richiama toString che chiama .join per le matrici.

Un'altra soluzione, utilizzando Symbol.toPrimitive che è un equivalente ES6 di toString/valueOf

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

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

458
georg

Se viene chiesto se è possibile (non DEVE), può chiedere a "a" di restituire un numero casuale. Sarebbe vero se genera 1, 2 e 3 in sequenza.

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

259
mmmaaa

Quando non puoi fare nulla senza le espressioni regolari:

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

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

Funziona a causa del metodo valueOf personalizzato che viene chiamato quando Object è paragonato al primitivo (come Number). Il trucco principale è che a.valueOf restituisce ogni volta un nuovo valore perché chiama exec su un'espressione regolare con g flag, che causa l'aggiornamento di lastIndex di quell'espressione regolare ogni volta che viene trovata una corrispondenza. Quindi, per la prima volta this.r.lastIndex == 0, corrisponde a 1 e aggiorna lastIndex: this.r.lastIndex == 1, quindi la regex successiva corrisponderà a 2 e così via.

203
Kos

Può essere realizzato utilizzando quanto segue nell'ambito globale. Per nodejs usa global invece di window nel codice qui sotto.

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

Questa risposta abusa delle variabili implicite fornite dall'ambito globale nel contesto di esecuzione definendo un getter per recuperare la variabile.

186
jontro

Ciò è possibile nel caso in cui la variabile a sia accessibile, ad esempio 2 web worker attraverso un SharedArrayBuffer, così come alcuni script principali. La possibilità è bassa, ma è possibile che quando il codice è compilato in codice macchina, i web worker aggiornino la variabile a appena in tempo, quindi le condizioni a==1, a==2 e a==3 sono soddisfatte.

Questo può essere un esempio di condizioni di competizione nell'ambiente multi-thread fornito da web worker e SharedArrayBuffer in JavaScript.

Ecco l'implementazione di base di cui sopra:

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

Sul mio MacBook Air, succede dopo circa 10 miliardi di iterazioni al primo tentativo:

 enter image description here

Secondo tentativo:

 enter image description here

Come ho detto, le probabilità saranno basse, ma dato il tempo sufficiente, colpirà la condizione.

Suggerimento: se il sistema impiega troppo tempo. Prova solo a == 1 && a == 2 e cambia Math.random()*3 in Math.random()*2. Aggiungendo sempre più elenchi si elimina la possibilità di colpire.

182
mehulmpt

Questo è anche possibile utilizzando una serie di getter auto-sovrascrittura:

(Questo è simile alla soluzione di jontro, ma non richiede una variabile contatore).

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

145
Patrick Dark

Non vedo questa risposta già pubblicata, quindi aggiungerò anche questa. Questo è simile a La risposta di Jeff con lo spazio di Hangul a mezza larghezza.

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

Potresti notare una leggera discrepanza con il secondo, ma il primo e il terzo sono identici a occhio nudo. Tutti e 3 sono caratteri distinti:

a - Minuscolo latino A
- Full Width Latin minuscolo A
а - lettera minuscola cirillica A

Il termine generico per questo è "omografi": diversi caratteri Unicode che sembrano uguali. Tipicamente difficili da ottenere tre che sono assolutamente indistinguibili, ma in alcuni casi puoi essere fortunato. A, Α, А e Ꭺ funzionerebbero meglio (Latin-A, Greek Alpha , Cyrillic-A , e Cherokee-A rispettivamente; purtroppo le lettere minuscole di greco e Cherokee sono troppo diversi dal latino a: α, , e quindi non aiuta con lo snippet precedente).

Esiste un'intera classe di attacchi agli omogei, più comunemente nei nomi di dominio fasulli (ad esempio wikipediа.org (cirillico) vs wikipedia.org (latino)), ma può essere visualizzato anche nel codice; tipicamente indicato come subdolo (come menzionato in un commento, [sottotenente] le domande sono ora fuori tema su PPCG , ma usato per essere un tipo di sfida in cui questo genere di cose mostrerebbe su). Ho usato questo sito web per trovare gli omografi usati per questa risposta.

127
Draco18s

In alternativa, è possibile utilizzare una classe per esso e un'istanza per il controllo.

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

var a = new A;

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

MODIFICARE

Usando le classi ES6 sembrerebbe questo

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

123
Nina Scholz

JavaScript

a == a +1

In JavaScript, non ci sono interi ma solo Numbers, che sono implementati come numeri in virgola mobile a precisione doppia.

Significa che se un numero a è sufficientemente grande, può essere considerato uguale a tre interi consecutivi:

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

È vero, non è esattamente quello che ha chiesto l'intervistatore (non funziona con a=0), ma non comporta alcun trucco con funzioni nascoste o sovraccarico dell'operatore.

Altre lingue

Per riferimento, ci sono soluzioni a==1 && a==2 && a==3 in Ruby e Python. Con una leggera modifica, è anche possibile in Java.

Rubino

Con un == personalizzato:

class A
  def ==(o)
    true
  end
end

a = A.new

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

O un a crescente:

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

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

Pitone

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

Giava

È possibile modificare Java Integer cache :

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

Sì, è possibile! ????

»JavaScript

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

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

Il codice sopra è una versione breve (grazie a @ Forivin per la nota nei commenti) e il seguente codice è originale:

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 vedi solo il lato superiore del mio codice ed eseguilo, dici WOW, come?

Quindi penso che sia sufficiente dire Sì, è possibile a qualcuno che ha detto di tu: Niente è impossibile

Trucco: ho usato un carattere nascosto dopo if per fare una funzione che il suo nome è simile a if. In JavaScript non possiamo sovrascrivere le parole chiave, quindi sono costretto a utilizzare in questo modo. È un if falso, ma funziona in questo caso per te!


»C #

Inoltre ho scritto una versione C # (con incremento della proprietà valore 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!????");
    }
}

Dimostrazione dal vivo

90
RAM

Questa è una versione invertita di @ Jeff's answer * dove un carattere nascosto (U + 115F, U + 1160 o U + 3164) viene usato per creare variabili che assomigliano a 1, 2 e 3.

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

* Questa risposta può essere semplificata usando zero width non-joiner (U + 200C) e zero width joiner (U + 200D). Entrambi questi caratteri sono consentiti all'interno degli identificatori ma non all'inizio:

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

Altri trucchi sono possibili usando la stessa idea, ad es. usando selettori di variazione Unicode per creare variabili che sembrano esattamente uguali (a︀ = 1; a︁ = 2; a︀ == 1 && a︁ == 2; // true).

78
Salman A

Regola numero uno delle interviste; mai dire impossibile.

Non c'è bisogno di trucchi nascosti per i personaggi.

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

72
MonkeyZeus

Sinceramente, se esiste un modo per valutare se è vero o no (e come altri hanno dimostrato, ci sono molti modi), la risposta che cercherò, parlando come qualcuno che ha condotto centinaia di interviste, sarebbe qualcosa sulla falsariga di:

"Beh, forse sì in alcune circostanze strane che non sono immediatamente ovvie per me ... ma se avessi incontrato questo in codice reale avrei usato le tecniche di debug più comuni per capire come e perché stava facendo quello che stava facendo e poi immediatamente refactoring il codice per evitare quella situazione ... ma ancora più importante: non scriverò MAI assolutamente quel codice in primo luogo perché questa è la definizione stessa di codice contorto, e mi sforzo di non scrivere mai codice contorto ".

Immagino che alcuni intervistatori si offenderebbero per avere ciò che è ovviamente destinato a essere una domanda molto complicata, ma non mi importa degli sviluppatori che hanno un'opinione, specialmente quando possono eseguire il backup con un pensiero ragionato e possono integrare la mia domanda in una dichiarazione significativa su se stessi.

66

Ecco un'altra variazione, utilizzando un array per mostrare i valori desiderati.

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

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

41
Théophile

Se ti capita di avere una domanda del genere (o di notare un comportamento altrettanto inaspettato nel tuo codice), pensa a quale tipo di cose potrebbe causare un comportamento che sembra impossibile a prima vista:

  1. Encoding: In questo caso la variabile che stai guardando non è quella che pensi sia. Questo può accadere se intenzionalmente ti godi con Unicode usando omoglyphs o space characters per far apparire il nome di una variabile come un altro, ma i problemi di codifica possono anche essere introdotti accidentalmente, per es. quando copi e incolla il codice dal Web che contiene punti di codice Unicode imprevisti (ad esempio perché un sistema di gestione dei contenuti ha eseguito alcune "formattazioni automatiche" come la sostituzione di fl con Unicode "LATIN SMALL LIGATURE FL" (U + FB02)).

  2. Condizioni di gara: A race condition potrebbe verificarsi, cioè una situazione in cui il codice non è in esecuzione nella sequenza prevista dallo sviluppatore. Le condizioni di gara spesso si verificano nel codice a più thread, ma non è necessario che più thread siano possibili: l'asincronicità è sufficiente (e non confondersi, async non significa che più thread sono usati sotto il cofano ). 

    Si noti che, pertanto, JavaScript non è esente da condizioni di gara solo perché è a thread singolo. Vedi qui per un semplice esempio a thread singolo, ma asincrono. Nel contesto di una singola affermazione, tuttavia, la condizione di gara sarebbe piuttosto difficile da colpire in JavaScript.

    JavaScript con i web worker è un po 'diverso, dato che puoi avere più thread. @mehulmpt ci ha mostrato un ottimo proof-of-concept usando i web worker .

  3. Effetti collaterali: un effetto collaterale dell'operazione di comparazione dell'uguaglianza (che non deve essere così ovvio come negli esempi qui, spesso gli effetti collaterali sono molto sottili). 

Questi tipi di problemi possono apparire in molti linguaggi di programmazione, non solo JavaScript, quindi non stiamo vedendo uno dei classici JavaScript WTF qui1

Certo, la domanda dell'intervista e gli esempi qui sembrano tutti molto artificiosi. Ma sono un buon promemoria che:

  • Gli effetti collaterali possono diventare davvero sgradevoli e che un programma ben progettato dovrebbe essere privo di effetti collaterali indesiderati.
  • Lo stato multi-threading e mutabile può essere problematico.
  • Non fare codifica dei caratteri e il corretto trattamento delle stringhe può portare a bug sgradevoli.

1 Ad esempio, puoi trovare un esempio in un linguaggio di programmazione totalmente diverso (C #) che mostra un effetto collaterale (ovvio) qui .

37
Dirk Vollmar

Ok, un altro hack con i generatori:

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

31
BaggersIO

In realtà la risposta alla prima parte della domanda è "Sì" in ogni linguaggio di programmazione. Ad esempio, questo è nel caso di 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

Utilizzo di Proxy :

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

I proxy fondamentalmente fingono di essere un oggetto di destinazione (il primo parametro), ma intercettano le operazioni sull'oggetto di destinazione (in questo caso l'operazione "ottieni proprietà") in modo che vi sia l'opportunità di fare qualcosa di diverso dal comportamento dell'oggetto predefinito. In questo caso l'azione "get property" viene chiamata su a quando == ne costringe il tipo per confrontarlo con ciascun numero. Questo succede:

  1. Creiamo un oggetto di destinazione, { i: 0 }, dove la proprietà i è il nostro contatore
  2. Creiamo un Proxy per l'oggetto di destinazione e lo assegniamo a a
  3. Per ogni confronto a ==, il tipo a è forzato a un valore primitivo
  4. Questo tipo di coercizione provoca la chiamata a[Symbol.toPrimitive]() internamente
  5. Il proxy intercetta l'ottenimento della funzione a[Symbol.toPrimitive] utilizzando il "gestore get"
  6. Il "get handler" del Proxy controlla che la proprietà ottenuta sia Symbol.toPrimitive, nel qual caso incrementa e restituisce il contatore dall'oggetto di destinazione: ++target.i. Se viene recuperata una proprietà diversa, torniamo indietro a restituire il valore predefinito della proprietà, target[name]

Così:

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

Come con la maggior parte delle altre risposte, questo funziona solo con un controllo di uguaglianza libera (==), poiché i controlli di uguaglianza rigorosa (===) non eseguono la coercizione di tipo che il Proxy può intercettare.

27
IceCreamYou

Stesso, ma diverso, ma sempre uguale (può essere "testato" più volte):

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

La mia idea è partita da come funziona l'equazione del tipo di oggetto Numero.

26
Preda7or

Una risposta ECMAScript 6 che utilizza i simboli:

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

A causa dell'uso di ==, JavaScript dovrebbe forzare a in qualcosa di simile al secondo operando (1, 2, 3 in questo caso). Ma prima che JavaScript provi a immaginare la coercizione da sola, prova a chiamare Symbol.toPrimitive . Se fornisci Symbol.toPrimitive JavaScript utilizzerà il valore restituito dalla funzione. In caso contrario, JavaScript chiamerebbe valueOf .

23
Omar Alshaker

Penso che questo sia il codice minimo per implementarlo:

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

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

Creazione di un oggetto fittizio con un valueOf personalizzato che incrementa una variabile globale i per ogni chiamata. 23 personaggi!

23
Gaafar

Questo usa la defineProperty con un effetto collaterale Nice che causa una variabile globale!

var _a = 1

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

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

11
Ben Aubin

Sovrascrivendo valueOf in una dichiarazione di classe, può essere fatto:

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

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

const a = new Thing();

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

Quello che succede è che valueOf viene chiamato in ogni operatore di confronto. Sul primo, a sarà uguale a 1, nel secondo, a sarà uguale a 2, e così via e così via, perché ogni volta che valueOf viene chiamata, il valore di a viene incrementato.

Pertanto console.log attiverà ed emetterà (sempre nel mio terminale) Thing: { value: 4}, indicando che il condizionale era vero.

0
Jonathan Kuhl