Pytanie Czy (a == 1 && a == 2 && a == 3) kiedykolwiek jest prawdziwe?


Notatka moderatora: Odrzuć chęć edytowania kodu lub usunięcia tego powiadomienia. Wzór białych znaków może być częścią pytania i dlatego nie powinien być niepotrzebnie naruszany. Jeśli jesteś w obozie "białych znaków", powinieneś być w stanie zaakceptować kod w niezmienionym stanie.

Czy to możliwe? (a== 1 && a ==2 && a==3) mógł ocenić true w JavaScript?

To jest pytanie do wywiadu zadane przez dużą firmę technologiczną. Zdarzyło się to dwa tygodnie temu, ale wciąż próbuję znaleźć odpowiedź. Wiem, że nigdy nie piszemy takiego kodu w naszej codziennej pracy, ale jestem ciekawy.


2253
2018-01-15 20:20


pochodzenie


To fantastyczne pytanie do wywiadu. Nigdy nie spotkałem się z tym (lub tym podobnym) samemu, ale jest o wiele lepszy niż ten, o którym słyszałem w związku ze znalezieniem pętli na połączonych listach. Jest wciąż podstępny i nie dyskwalifikuję nikogo na ten temat, ale z pewnością faworyzowałbym kandydatów, którzy mogliby rozpoznać, że coś podstępnego może się dziać. - Draco18s
Jeśli jest to trochę reprezentatywne dla rodzaju kodu, który mieszka w bazie kodu tej firmy ... uruchom ️ - FeifanZ
To po prostu straszne pytanie do wywiadu ... - ghord
Do ludzi, którzy najwyraźniej głosowali na cloae to jako zbyt szeroki: czy to jest skrót w JavaScript, że jest zbyt wiele poprawnych odpowiedzi? - tomsmeding
Notatka moderatora: Stack Overflow miał historię ludzi, którzy porozumiewają się z odpowiedziami w różnych językach na ten, o którym mowa. Te są próbuje odpowiedzieć na to pytanie, ponieważ są one rozwiązaniem ogólnego problemu, aczkolwiek w innym języku. Prosimy powstrzymać się od oznaczania ich jako "nie jest odpowiedzią". Powiedziawszy to, proszę również powstrzymać się od publikowania kolejnych odpowiedzi w różnych językach - jest powód, dla którego to pytanie jest specyficzne dla JavaScript, jak wskazano w komentarzach pod niektórymi innymi odpowiedziami, i jest powód, dla którego lubimy nasze pytania dotyczące języka tak pozostać. - BoltClock♦


Odpowiedzi:


Jeśli skorzystasz w jaki sposób == Prace, możesz po prostu utworzyć obiekt z niestandardowym toString (lub valueOf) funkcja, która zmienia to, co zwraca za każdym razem, gdy jest używana tak, że spełnia wszystkie trzy warunki.

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

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


Powód tego działania wynika z użycia luźnego operatora równości. Gdy używasz luźnej równości, jeśli jeden z operandów jest innego typu niż inny, silnik spróbuje przekonwertować jeden na drugi. W przypadku obiektu po lewej i liczby po prawej, spróbuje przekształcić obiekt na liczbę przez pierwsze wywołanie valueOf jeśli jest to wywołanie, a jeśli się nie uda, zadzwoni toString. użyłem toString w tym przypadku po prostu dlatego, że przyszło mi do głowy, valueOf miałoby więcej sensu. Jeśli zamiast tego zwróciłem ciąg znaków z toString, silnik próbowałby wówczas przekonwertować strunę na liczbę dającą nam ten sam efekt końcowy, choć z nieco dłuższą ścieżką.


3097
2018-01-15 20:35



Mógłbyś to osiągnąć zmieniając domniemane valueOf() operacja? - Sterling Archer
Tak, valueOf działa zamiast toString z tego samego powodu - Kevin B
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa była przeniesiono do czatu. - deceze♦
Według to najpierw zostanie wypróbowana konwersja liczb valueOf jest nieco lepszy. - Salman A
@Pureferret lewą stroną porównania równości jest obiekt, a nie liczba. Że ten obiekt ma właściwość liczbową inie przeszkadza silnikowi. ;) - tomsmeding


Nie mogłem się oprzeć - inne odpowiedzi są niewątpliwie prawdziwe, ale naprawdę nie można przejść obok następującego kodu:

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

Zauważ dziwne odstępy w if oświadczenie (które skopiowałem z twojego pytania). Jest to Hangul o połowie szerokości (to jest koreański dla tych, którzy nie są zaznajomieni), który jest znakiem spacji Unicode, który nie jest interpretowany przez skrypt ECMA jako znak spacji - oznacza to, że jest to poprawny znak dla identyfikatora. Dlatego są trzy zupełnie różne zmienne, jedna z Hangulem po a, jedna z nim przedtem i ostatnia z tylko. Zastępowanie przestrzeni za pomocą _ dla czytelności ten sam kod wyglądałby tak:

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

Sprawdzić walidacja walidatora nazw zmiennych Mathiasa. Jeśli te dziwne odstępy zostały uwzględnione w ich pytaniu, to jestem pewien, że jest to wskazówka dla tego rodzaju odpowiedzi.

Nie rób tego. Poważnie.

Edycja: Przyszło mi do głowy, że (chociaż nie wolno mu uruchamiać zmiennej) Frezarka o szerokości zero i Bez łączeń o zerowej szerokości znaki są również dozwolone w nazwach zmiennych - patrz Obfuskacja JavaScriptu o zerowej szerokości znaków - plusy i minusy?.

Wyglądałoby to tak:

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


1917
2018-01-16 05:14



Sądząc po dziwnym odstępie w pierwotnym pytaniu, myślę, że jest to DOKŁADNIE odpowiedź, której szukało pytanie wywiadu - wykorzystywanie nie-przestrzennych znaków, które wyglądają jak spacje. Dobre miejsce! - Baracus
@Baracus To RonJohn zauważył dziwne odstępy w swoim komentarzu na temat odpowiedzi Kevina, która przypomniała mi o tej (okropnej) technice, więc nie mogę wziąć uznania za to, że ją dostrzegłem. Byłem jednak zaskoczony, że nikt już na nie nie odpowiedział, ponieważ kilka lat temu obchodził moją pracę z powodu blogu gdzieś - przypuszczałem, że do tej pory było to dość powszechne. - Jeff
Oczywiście jest to zabronione jako standardowa luka, co dotyczy również wywiadów. [wymagany cytat] - Sanchises
Biorąc pod uwagę pierwotny odstęp, może być jeszcze gorszy, tj. Zmienna var ᅠ2 = 3 był użyty; więc są trzy zmienne aᅠᅠ= 1, ᅠ2 = 3, a = 3 (a␣ = 1, ␣2 = 3, a = 3, więc to (a␣==1 && a==␣2 && a==3)) ... - Holger
@EdmundReed: Mogę sobie wyobrazić błędną postać, która dostanie się do kodu źródłowego, a potem każdy ma diabła z pracy, próbującego znaleźć błąd. (Szczególnie, jeśli ktoś kodował w Korei.) - Martin Bonner


TO JEST MOŻLIWE!

var i = 0;

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

Wykorzystuje to getter wewnątrz a with zeznanie do wynajęcia a ocenić do trzech różnych wartości.

... to nadal nie oznacza, że ​​powinno się używać tego w prawdziwym kodzie ...


567
2018-01-15 20:35



Tak, próbowałem tego samego :) Tak więc poprawna odpowiedź w wywiadzie brzmiałaby: "Nie może się wydarzyć mój kod, ponieważ nigdy nie używam with. " - Pointy
@Pointy - I, programuję w trybie ścisłym, gdzie with nie jest dozwolone. - jfriend00
@Pointy w przyjętej odpowiedzi robią coś podobnego bez with więc może się zdarzyć - Jungkook
@ JonasW. Wiele osób nadal używa == ale nie widziałem with od ... właściwie nigdy poza dokumentacją JS, gdzie jest napisane "proszę, nie używaj tego". W każdym razie fajne rozwiązanie. - wortwart
@Pointy Możesz to zrobić bez with: stackoverflow.com/a/48288170/65387 - mpen


Przykład bez getterów lub valueOf:

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

Działa to, ponieważ == przywołuje toString która dzwoni .join dla Array.

Inne rozwiązanie, używając Symbol.toPrimitive który jest odpowiednikiem ES6 toString/valueOf:

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

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


431
2018-01-17 11:37



without valueOf, cóż ... jest bardziej pośredni, ale w zasadzie to samo. - Jonas Wilms
Naprawdę podoba mi się to rozwiązanie, ponieważ nie przesłonię niczego poza funkcją łączenia obiektów, a jest to po prostu bardzo czysty i łatwy do odczytania hack, który sprawia, że ​​logika jest prawdziwa. - Alex Pedersen
Szczerze myślę, że to najlepsza odpowiedź. Nie wiąże się z niczym niezwykłym, po prostu ustawiając kilka wartości. Bardzo łatwy do zrozumienia, nawet przy podstawowej wiedzy JS. Dobra robota. - Zac Delventhal
To ma tak wielki sens, że niemal się przydaje. - Andrew
Wiedziałem, że większość odpowiedzi będzie dotyczyć nadużywania toString lub valueOf ale ten przyłapał mnie zupełnie na baczności. Bardzo sprytny i nie wiedziałem, że zadzwonił .joinwewnętrznie, ale ma to sens. - GBarroso


Jeśli zostanie zapytany, czy jest to możliwe (NIE MUSI), może poprosić "a" o zwrot losowej liczby. Byłoby prawdą, gdyby generował kolejno 1, 2 i 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;
    }
  }
}


249
2018-01-16 06:21



Horray na --bogosferę - bogogenerację? - Baldrickk
Celowo udzieliłbym tej odpowiedzi, nawet gdybym znał inne rozwiązania, ponieważ odpowiada na pytanie, ale oczywiście nie jest to, o co im chodziło. Graj głupie gry, wygrywaj głupie nagrody. - Edmund Reed
Ale co, jeśli potrzeba więcej niż 1000 prób? - Piyin
@Piyin Jeśli potrzeba więcej niż 1000 prób, wygrywasz nagrodę! - Skeets
Podoba mi się ta odpowiedź, ponieważ podejście do ekstremum sugeruje, że jest to możliwe w każdy język, jeśli rejestry / pamięć podręczna cpu zostaną uderzone wystarczającą ilością promieni kosmicznych podczas działania programu lub gdy ktoś umyślnie wykona usterkę zasilania, tak że gałąź niepowodzeń warunku warunkowego w rzeczywistości nie skacze. - Ponkadoodle


Kiedy nie możesz nic zrobić bez wyrażeń regularnych:

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

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

Działa z powodu niestandardowego valueOf metoda, która jest wywoływana, gdy obiekt jest porównywany z prymitywem (takim jak liczba). Główna sztuczka polega na tym a.valueOf zwraca za każdym razem nową wartość, ponieważ dzwoni exec na wyrażenie regularne z g flagę, która powoduje aktualizację lastIndex tego wyrażenia regularnego za każdym razem, gdy znaleziono dopasowanie. Pierwszy raz this.r.lastIndex == 0, to pasuje 1 i aktualizacje lastIndex: this.r.lastIndex == 1, więc następnym razem dopasuje się wyrażenie regularne 2 i tak dalej.


196
2018-01-16 19:35



Umm .. jak to działa? - Abdillah
@Abdillah obiekt regex zapamięta ostatni indeks, który pasuje, wywołanie exec ponownie rozpocznie wyszukiwanie z tego indeksu. MDN nie jest bardzo jasne. - Simon Chan
Rozumiem, więc this.r obiekt regex zapamiętaj stan / indeks. Dzięki! - Abdillah
! - Patrick Roberts
Polecam przekazać ciąg do exec nie jest to jednak liczba całkowita, która ma być usztywniona. - Bergi


Można to osiągnąć za pomocą następujących w zakresie globalnym. Dla nodejs posługiwać się global zamiast window w kodzie poniżej.

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

Ta odpowiedź narusza niejawne zmienne dostarczone przez globalny zakres w kontekście wykonania, definiując program pobierający w celu pobrania zmiennej.


183
2018-01-15 20:37



To zakłada a jest własnością this które nie wydają się być. Gdyby a była zmienną lokalną (jak to wygląda), to by nie działało. - jfriend00
@ jfriend00 masz na myśli, jeśli umieściłeś var a; gdzieś? - jontro
Tak. Referencje a == 1 implikuje niż a jest gdzieś zmienną, a nie właściwością this. Chociaż istnieje dziwne miejsce, takie jak globale, gdzie obie mogą być prawdziwe, ogólnie mówiąc, deklarując zmienną var a lub let a oznacza, że ​​nie ma this to pozwala ci uzyskać dostęp a jako właściwość taka jak Ty zakładasz kod. Twój kod najwyraźniej przybiera jakąś dziwną zmienną globalną. Na przykład Twój kod nie działa w węźle node.js, a nie w trybie ścisłym wewnątrz funkcji. Powinieneś dokładnie określić okoliczności, w których on działa i prawdopodobnie wyjaśnić, dlaczego to działa. W przeciwnym razie jest to mylące. - jfriend00
@ jfriend00 dobrze. Nie jestem pewien, czy dodałoby to znacznie więcej wartości w połączeniu z innymi już odpowiedziami. Zaktualizuje odpowiedź - jontro
Pytanie brzmiało, czy to "kiedykolwiek" może być prawdą. Odpowiedź brzmi: tak, i jest to jeden ze scenariuszy, w którym może to być prawda: a nie jest zmienną lokalną i jest zdefiniowany w zasięgu globalnym za pomocą inkrementującego gettera. - Zac Delventhal