Vraag Kan (a == 1 && a == 2 && a == 3) ooit evalueren of het waar is?


Notitie van de moderator: Verwerp de drang om de code te bewerken of verwijder deze kennisgeving. Het patroon van witruimte kan deel uitmaken van de vraag en daarom mag niet onnodig worden geknoeid. Als u zich in het "witruimte is onbeduidend" kamp bevindt, zou u de code moeten kunnen accepteren zoals deze is.

Is het ooit mogelijk dat (a== 1 && a ==2 && a==3) zou kunnen evalueren true in JavaScript?

Dit is een interviewvraag van een groot technologiebedrijf. Het gebeurde twee weken geleden, maar ik probeer nog steeds het antwoord te vinden. Ik weet dat we nooit zo'n code schrijven in onze dagelijkse klus, maar ik ben nieuwsgierig.


2250
2018-01-15 20:20


oorsprong


antwoorden:


Als je misbruik maakt van hoe == werken, je zou eenvoudig een object met een aangepast kunnen maken toString (of valueOf) functie die verandert wat het teruggeeft elke keer dat het wordt gebruikt, zodat het voldoet aan alle drie voorwaarden.

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

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


De reden waarom dit werkt is te wijten aan het gebruik van de operator voor losse gelijkheid. Bij gebruik van losse gelijkheid, als een van de operanden van een ander type is dan de andere, zal de motor proberen de ene naar de andere om te zetten. In het geval van een object aan de linkerkant en een cijfer aan de rechterkant, zal het proberen het object naar een nummer te converteren door eerst te callen valueOf als het callable is, en als dat niet lukt, zal het callen toString. ik gebruikte toString in dit geval, gewoon omdat het is wat in je opkwam, valueOf zou logischer zijn. Als ik in plaats daarvan een string terugstuurde van toString, de motor zou dan geprobeerd hebben om de snaar naar een getal om te zetten, wat ons hetzelfde eindresultaat oplevert, hoewel met een iets langer pad.


3091
2018-01-15 20:35



Ik kon het niet laten - de andere antwoorden zijn ongetwijfeld waar, maar je kunt echt niet voorbij de volgende code lopen:

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

Let op de rare afstand in de if verklaring (die ik heb gekopieerd van uw vraag). Het is de Hangul met halve breedte (Koreaans voor diegenen die niet bekend zijn), een Unicode-spatiesymbool dat niet wordt geïnterpreteerd door ECMA-script als een spatiesymbool - dit betekent dat het een geldig teken is voor een identifier. Daarom zijn er drie totaal verschillende variabelen, één met de Hangul na de a, één ermee eerder en de laatste met slechts een. De ruimte vervangen door _ voor leesbaarheid zou dezelfde code er als volgt uitzien:

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

Uitchecken de validatie op Mathias 'variable name validator. Als die rare spatiëring eigenlijk in hun vraag was opgenomen, ben ik er zeker van dat het een hint is voor dit soort antwoorden.

Doe dit niet. Ernstig.

Edit: Het is onder mijn aandacht gekomen dat (hoewel het niet toegestaan ​​is om een ​​variabele te starten) de Schrijnwerker zonder breedte en Niet-schrijnwerker zonder breedte tekens zijn ook toegestaan ​​in variabelenamen - zie JavaScript met nul-breedtekens camoufleren - voor- en nadelen?.

Dit zou er als volgt uitzien:

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


1914
2018-01-16 05:14



HET IS MOGELIJK!

var i = 0;

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

Dit gebruikt een getter in een with verklaring om te laten a evalueren naar drie verschillende waarden.

... dit betekent nog niet dat dit in echte code moet worden gebruikt ...


565
2018-01-15 20:35



Voorbeeld zonder getters of valueOf:

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

Dit werkt omdat == oproept toString welke oproepen .join voor arrays.

Een andere oplossing, met behulp van Symbol.toPrimitive wat een ES6-equivalent is van toString/valueOf:

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

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


428
2018-01-17 11:37



Als wordt gevraagd of het mogelijk is (niet MUST), kan het "a" vragen om een ​​willekeurig getal te retourneren. Het zou waar zijn als het achtereenvolgens 1, 2 en 3 genereert.

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


249
2018-01-16 06:21



Wanneer je niets kunt doen zonder reguliere expressies:

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

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

Het werkt vanwege de gewoonte valueOf methode die wordt aangeroepen wanneer Object wordt vergeleken met primitief (zoals Number). De hoofdtruc is dat a.valueOf geeft elke keer nieuwe waarde omdat het belt exec op reguliere expressie met g vlag, die een update veroorzaakt lastIndex van die reguliere expressie telkens wanneer een overeenkomst wordt gevonden. Dus de eerste keer this.r.lastIndex == 0, het komt overeen 1 en updates lastIndex: this.r.lastIndex == 1, dus de volgende keer dat regex overeenkomt 2 enzovoort.


195
2018-01-16 19:35



Het kan worden bereikt met behulp van het volgende in de globale scope. Voor nodejs gebruik global in plaats van window in de onderstaande code.

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

Dit antwoord maakt misbruik van de impliciete variabelen die door de globale scope in de uitvoeringscontext worden geboden door een getter te definiëren om de variabele op te halen.


183
2018-01-15 20:37



Dit is mogelijk in het geval van een variabele a wordt benaderd door, zeg 2 webwerkers via een SharedArrayBuffer evenals een hoofdscript. De mogelijkheid is laag, maar het is mogelijk dat wanneer de code wordt gecompileerd naar machinecode, de webwerkers de variabele bijwerken a net op tijd dus de omstandigheden a==1, a==2 en a==3 zijn tevreden.

Dit kan een voorbeeld zijn van de race-conditie in multi-threaded omgeving geboden door webwerkers en SharedArrayBuffer in JavaScript.

Hier is de basisimplementatie van hierboven:

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

Op mijn MacBook Air gebeurt het na ongeveer 10 miljard iteraties bij de eerste poging:

enter image description here

Tweede poging:

enter image description here

Zoals ik al zei, de kansen zijn laag, maar als er genoeg tijd is, raakt het de voorwaarde.

Tip: als het te lang duurt op uw systeem. Probeer alleen a == 1 && a == 2 en veranderen Math.random()*3 naar Math.random()*2. Door meer en meer toe te voegen aan de lijst, wordt de kans op een slag kleiner.


171
2018-01-17 07:39



Dit is ook mogelijk met behulp van een reeks zelfoverschrijvende getters:

(Dit is vergelijkbaar met de oplossing van jontro, maar vereist geen tellervariabele.)

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



Ik zie dit antwoord niet al gepost, dus ik zal deze ook in de mix gooien. Dit is vergelijkbaar met Het antwoord van Jeff met de Halve breedte Hangul-ruimte.

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

Mogelijk merkt u een lichte discrepantie met de tweede, maar de eerste en derde zijn identiek voor het blote oog. Alle drie zijn verschillende karakters:

a - Latijnse kleine letters A
 - Volledige breedte Latin kleine letters A
а - Cyrillische kleine letters A

De algemene term hiervoor is 'homoglyphs': verschillende Unicode-tekens die er hetzelfde uitzien. Meestal moeilijk te krijgen drie die niet te onderscheiden zijn, maar in sommige gevallen kunt u geluk hebben. A, Α, А en Ꭺ werken beter (Latin-A, Griekse Alpha, Cyrillic-A, en Cherokee-A respectievelijk; helaas zijn de Griekse en Cherokee kleine letters te verschillend van het Latijn a: α,, en dus helpt het niet met het bovenstaande fragment).

Er is een hele klasse van Homoglyph-aanvallen, meestal in nep-domeinnamen (bijv. wikipediа.org (Cyrillisch) vs wikipedia.org (Latijn)), maar het kan ook in de code verschijnen; meestal aangeduid als achterbaks (zoals vermeld in een opmerking, [Achterbaks] vragen zijn nu off-topic op PPCG, maar was vroeger een soort uitdaging waar dit soort dingen zouden verschijnen). ik gebruikte deze website om de homogliefen te vinden die voor dit antwoord zijn gebruikt.


125
2018-01-16 18:44



Je kunt ook een klasse hiervoor gebruiken en een instantie voor de controle.

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

var a = new A;

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

BEWERK

Met ES6-klassen zou het er zo uitzien

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