Domanda 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. Lo schema degli spazi bianchi 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) potrebbe 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.


2249
2018-01-15 20:20


origine


risposte:


Se ne approfitti Come == lavori, potresti semplicemente creare un oggetto con un'abitudine toString (o valueOf) funzione che cambia ciò che restituisce ogni volta che viene utilizzato 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!');
}


La ragione per cui funziona è dovuta 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 prima chiamando valueOf se è callabile, e in caso contrario, chiamerà toString. ero solito toString in questo caso semplicemente perché è quello che mi è venuto in mente, valueOf avrebbe più senso Se invece restituissi una stringa 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.


3090
2018-01-15 20:35



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 nel if dichiarazione (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 viene 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. Sostituire 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!")
}

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

Non farlo. Sul serio.

Edit: Mi è venuto in mente che (anche se non è consentito avviare una variabile) il Falegname a larghezza zero e Non-falegname a larghezza zero i caratteri sono ammessi anche nei nomi delle variabili - vedi Offuscare 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!")
}


1913
2018-01-16 05:14



È 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 a with dichiarazione da lasciare a valutare a tre valori diversi.

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


565
2018-01-15 20:35



Esempio senza getters o valueOf:

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

Funziona perché == invoca toString che chiama .join per gli array.

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

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

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


427
2018-01-17 11:37



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


248
2018-01-16 06:21



Quando non puoi fare nulla senza 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 di abitudine valueOf metodo che viene chiamato quando Object rispetto a primitive (come Number). Il trucco principale è quello a.valueOf restituisce un nuovo valore ogni volta perché chiama exec sull'espressione regolare con g flag, che causa l'aggiornamento lastIndex di quell'espressione regolare ogni volta che viene trovata una corrispondenza. Quindi la prima volta this.r.lastIndex == 0, corrisponde 1 e aggiornamenti lastIndex: this.r.lastIndex == 1quindi la prossima regex corrisponderà 2 e così via.


195
2018-01-16 19:35



Può essere realizzato utilizzando quanto segue nell'ambito globale. Per nodejs uso 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.


183
2018-01-15 20:37



Questo è possibile in caso di variabile a a cui si accede, ad esempio 2 web worker attraverso un SharedArrayBuffer e alcuni script principali. La possibilità è bassa, ma è possibile che quando il codice è compilato su un codice macchina, i web worker aggiornino la variabile a appena in tempo, quindi le condizioni a==1, a==2 e a==3 sono soddisfatti.

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 necessario, colpirà la condizione.

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


171
2018-01-17 07:39



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


141
2018-01-16 11:37



Non vedo questa risposta già pubblicata, quindi aggiungerò anche questa. Questo è simile a La risposta di Jeff con lo spazio dell'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
 - Minuscolo latino a lettera intera A
а - lettera minuscola cirillica A

Il termine generico per questo è "omografi": diversi caratteri Unicode che sembrano uguali. In genere difficile da ottenere tre che sono assolutamente indistinguibili, ma in alcuni casi puoi essere fortunato. A, Α, А e Ꭺ avrebbero funzionato meglio (Latin-A, Alfa greca, Cirillico-A, e Cherokee-A rispettivamente; sfortunatamente le lettere minuscole di greco e cherokee sono troppo diverse dal latino a: α,e quindi non aiuta con il frammento di cui sopra).

C'è un'intera classe di attacchi agli omogei là fuori, più comunemente in nomi di dominio falsi (es. wikipediа.org (Cirillico) vs wikipedia.org (Latino)), ma può essere visualizzato anche in codice; tipicamente indicato come subdolo (come menzionato in un commento, [Subdola] le domande sono ora fuori tema PPCG, ma era un tipo di sfida in cui comparire questo genere di cose). ero solito questo sito web per trovare gli omografi usati per questa risposta.


125
2018-01-16 18:44



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


112
2018-01-16 15:11