Dlaczego! {} [True] przyjmuje wartość true w JavaScript?

131

{}[true]jest [true]i ![true]powinno być false.

Dlaczego więc !{}[true]ocenia się true?

user2430508
źródło
30
var o = {}; o[true] === undefined.
azz
2
Wyjaśnienie tutaj będzie prawdopodobnie bardzo podobne do osobliwości omówionych w poprzednim pytaniu
IMSoP
45
„Ponieważ Javascript jest głupi” prawdopodobnie nie jest odpowiedzią, której szukasz.
georg
2
Jak wspomniano, jeśli korzystasz {}[true] === [true]z konsoli, to dlatego, że traktuje ona {}jako pusty blok kodu, a nie obiekt.
azz
3
jeśli to pomoże, spróbuj porównać {}iw ({})konsoli (lub {}[true]i ({})[true]). Ponadto, jak nikt o tym nie wspomniał, obiekt [prawda] jest oceniany jako obiekt [„prawda”].
BiAiB

Odpowiedzi:

172

Uważam, że dzieje się tak, ponieważ zwykły {}[true]kod jest analizowany jako pusty blok instrukcji (nie literał obiektowy), po którym następuje tablica zawierająca true, czyli true.

Z drugiej strony zastosowanie !operatora powoduje, że analizator składni interpretuje {}jako literał obiektu, więc poniższy {}[true]kod staje się zwracanym dostępem do elementu członkowskiego undefinedi !{}[true]jest rzeczywiście true(taki, jaki !undefinedjest true).

Frédéric Hamidi
źródło
25
Z drugiej strony, fakt, że! Undefined jest prawdziwy, jest nadal niewybaczalny.
evilcandybag
87
@evilcandybag: Absolutnie nie. undefinedjest fałszywy (coś, na czym często polegamy - if (obj.maybeExists) ...), więc ma doskonały logiczny sens, że !undefinedto prawda.
josh3736
8
@Josh, myślę, że evilcandybag wolałaby zachowanie podobne do tego nullw niektórych językach, z !undefinedrównością undefined. Tak nie jest jednak w Javascript.
Frédéric Hamidi
6
@evilcandybag: logiczne jest jedynie stwierdzenie, że coś, co jest not undefined( !undefined), musi zatem zostać zdefiniowane. Jeśli coś jest zdefiniowane, jest zwykle interpretowane jako true.
OozeMeister
7
@Cruncher Jeśli a jest niezdefiniowane, a b jest niezdefiniowane, skąd możemy wiedzieć, że a! = B? Szczególnie, gdy jedyna znana cecha dwóch zmiennych jest dokładnie taka sama.
LJ2
44

Ponieważ {}[true]nie zwraca true, ale undefinedi undefinedjest oceniany jako false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true
dooxe
źródło
21
Jeśli oceniasz {}[true]w konsoli, otrzymujesz [true], ponieważ {}jest interpretowany jako pusty blok kodu, a nie obiekt. Chodzi o kontekst i niejednoznaczność {}.
IMSoP
1
@IMSoP, ale dlaczego {key:"value"}[1,2,3];ocenia się również [1,2,3]?
t.niese
3
@ t.niese, ponieważ jest analizowany jako blok instrukcji zawierający etykietę ( key:) i literał ciągu ( "value"), po którym następuje tablica. Parser nadal nie widzi literału obiektu.
Frédéric Hamidi
1
@ FrédéricHamidi ah tak, to wszystko.
Stłumiłem
1
@dooxe Przeczytaj pozostałe odpowiedzi; chodzi o kontekst, w jakim jest interpretowany. Jeśli owinąć go alert()lub console.log()lub przypisać ją do zmiennej powoduje zmianę kontekstu, dlatego, że nie zachowują się w ten sam sposób, jak wpisane na własną rękę w konsoli.
IMSoP
27

Dlatego

{}[true]

ocenia do undefinedi !undefinedjest true.

Od @schlingel:

truejest używany jako klucz i {}jako mapa skrótów. Nie istnieje właściwość z kluczem, truewięc zwraca undefined. Nie undefinedjest true, zgodnie z oczekiwaniami.

Sesja konsoli ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

Jednak w konsoli Google Chrome :

> !{}[true]
true

Więc bez niespójności. Prawdopodobnie używasz starej wersji maszyny wirtualnej JavaScript. Dla tych, którzy potrzebują dalszych dowodów:

Tutaj wprowadź opis obrazu

AKTUALIZACJA

W przeglądarce Firefox ocenia się również jako true:

Tutaj wprowadź opis obrazu

Gry Brainiac
źródło
Nie, jeśli to zrobisz eval('{}[true]')lub wpiszesz to do konsoli. Wtedy np. Als {}"test"jest testlub nawet {key:"value"}"test"jest test.
t.niese
Ciekawe, w którym silniku js to testujesz?
t.niese
@ t.niese Właśnie wpisałem go w konsoli węzła i oto, co otrzymałem.
Games Brainiac
Tylko z ciekawości. Czy {}[true];(z ;) wraca [true]za Ciebie, bo tutaj tak?
t.niese
2
Powód dla przeciwników? Jest prawie identyczna odpowiedź jak ta z 8 głosami, a ja otrzymałem głos przeciw? Co zrobiłem źle?
Games Brainiac
23

Przyczyną zamieszania jest niezrozumienie twojego pierwszego twierdzenia:

{}[true] jest [true]

To, co widzisz, gdy go uruchamiasz, jest wynikiem niejednoznaczności. Javascript ma zdefiniowany zestaw reguł, jak radzić sobie z takimi niejednoznacznościami, iw tym przypadku dzieli to, co widzisz jako instrukcję signle, na dwie oddzielne instrukcje.

Tak więc Javascript widzi powyższy kod jako dwie oddzielne instrukcje: Po pierwsze, istnieje a {}, a następnie jest całkowicie oddzielne [true]. Drugie stwierdzenie jest tym, co daje wynik [true]. Pierwsza instrukcja {}jest skutecznie całkowicie ignorowana.

Możesz to udowodnić, wykonując następujące czynności:

({}[true])

tj. zawijanie całości w nawiasy, aby zmusić tłumacza do przeczytania jej jako pojedynczej instrukcji.

Teraz zobaczysz, że rzeczywista wartość twojego wyciągu to undefined. (to również pomoże nam później zrozumieć następną część)

Teraz wiemy, że początkowa część Twojego pytania to czerwony śledź, przejdźmy więc do ostatniej części pytania:

Dlaczego więc! {} [True] jest przeliczane na prawda?

Tutaj mamy to samo stwierdzenie, ale z !dołączonym na początku.

W tym przypadku reguły JavaScript mówią mu, aby oceniał całość jako pojedynczą instrukcję.

Odwołaj się do tego, co się stało, gdy zawarliśmy wcześniejsze oświadczenie w nawiasach; mamy undefined. Tym razem skutecznie robimy to samo, ale stawiamy !przed tym. Więc twój kod może zostać uproszczony jako !undefined, który jest true.

Mam nadzieję, że to trochę wyjaśnia.

Jest to złożona bestia, ale lekcja, której należy się tutaj nauczyć, polega na używaniu nawiasów wokół instrukcji podczas oceny ich w konsoli, aby uniknąć takich fałszywych wyników.

Spudley
źródło
2
Nie sądzę, że {}[true]jest dokładnie nieważne , po prostu niejednoznaczne . Można go interpretować jako „pusty blok kodu, po którym następuje literał tablicy” lub „literał obiektowy bez właściwości, do których uzyskiwany jest dostęp”. Nie wiem, czy pierwszy jest technicznie przypadkiem ASI (wiele języków i tak nie wstawiłoby tam średnika), ale to interpretacja kontekstowa jest sednem problemu.
IMSoP
@IMSoP - już zredagowałem odpowiedź, zanim opublikowałeś komentarz. :)
Spudley
1
Nadal mówi, że „{} [prawda] w rzeczywistości nie jest w ogóle ważna” na początku odpowiedzi.
IMSoP
Ponadto OP nie powiedział „ {}[true]czy true”, powiedzieli „ {}[true]jest [true]”, co jest jedną z dwóch ważnych interpretacji niejednoznacznego stwierdzenia.
IMSoP
14

{}[true]jest undefined. Aby to znaleźć, napisz to:

a = {};
a[true] === undefined // true

lub po prostu:

({})[true] === undefined // true

Wiemy, że !undefinedto jest true.


Z odpowiedzi @Benjamina Gruenbauma :

Narzędzia Chrome Dveloper wykonują następujące czynności :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

Więc zasadniczo wykonuje a callna obiekcie z wyrażeniem. Wyrażenie to:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

Zatem, jak widać, wyrażenie jest ewaluowane bezpośrednio, bez nawiasów zawijających.

Więcej informacji można znaleźć w tym pytaniu .

Ionică Bizău
źródło
10

Odpowiedzi tutaj są dobre, oto podział pseudokodu:

  • {}['whatever'] = pusty blok, NewArray ('cokolwiek') = NewArray ('cokolwiek')
  • {}[true] = pusty blok, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.whewhere)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = Grupowanie (NowyObiekt.cokolwiek) = Grupowanie (niezdefiniowane) = niezdefiniowane
Ben Lesh
źródło
8

Dzieje się tak, ponieważ {}w twoim rozumieniu nie jest to dosłowna prezentacja Object, ale pusty zakres (lub pusty blok kodu):

{ var a = 1 }[true] // [true] (do the same thing)

Po prostu ocenia kod w zakresie, a następnie pokazuje twoją tablicę.

I od twojego

!{}[true]

Po prostu konwertuje do int w tym zakresie i zwraca tę samą tablicę true. W tym kodzie nie ma sprawdzeń typu bool.

A jeśli spróbujesz sprawdzić wynik od {}[true]Ciebie, otrzymasz false:

{}[true] -> [true] -> ![true] -> false

Ponieważ nie ma już żadnego zakresu.

Więc !w swoim pytaniu zrób to samo, co:

!function() {
   //...
}
antyrat
źródło
Jest to łatwiejsze do zauważenia, jeśli to zrobisz var x = {}; x[true].
Chris Hayes
1
Nie jestem pewien, co masz na myśli, mówiąc „konwertuje do tego zakresu”; Myślę, że z prowadzeniem !jest to interpretowane jako pusty obiekt, a nie zakres, i to jest rozbieżność.
IMSoP
6
  • {} jest obiektem bez właściwości.
  • Ponieważ []bezpośrednio następuje po obiekcie, oznacza to „Uzyskaj dostęp do właściwości o tej nazwie”, a nie „Utwórz tablicę”
  • truejest wartością logiczną, ale jest używana jako nazwa właściwości, więc jest rzutowana na ciąg ( "true")
  • Obiekt nie ma właściwości o nazwie true(ponieważ nie ma żadnych właściwości), więc {}['true']jestundefined
  • !undefinedrzutuje undefinedna wartość logiczną ( false)
  • Operator not zamienia się falsew true.
Quentin
źródło
2
W tym przypadku {}[true](bez innego związku) {}jest nie przedmiot bez właściwości, to pusta kodu.
IMSoP
4

Zagrajmy trochę więcej!

Po pierwsze, zabawmy się !:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

Ok, spróbujmy po kolei zrozumieć te szalone zachowania:

1) Tutaj {}jest analizowany jako pusty blok kodu. Bez przypisania, negacji, grupowania (z nawiasami) lub jakiejkolwiek składni, która wskazuje parserowi, że {}jest to literał obiektowy, domyślnym założeniem jest myślenie, że jest to po prostu bezużyteczny pusty blok.

Oto dowód takiego zachowania:

{ alert(123) }[true]

Powyższy kod pokaże alert normalnie i zostanie oceniony jako [true], w ten sam sposób {}[true].

Instrukcje blokowe bez średników

Instrukcja typu blokowego nie potrzebuje po niej średnika.

Na przykład:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

Wyświetlane są oba alerty.

Widzimy więc, że pusta instrukcja blokowa bez średnika jest ważna i po prostu nic nie robi. W ten sposób, gdy wejdziesz {}[true]do konsoli narzędzi programistycznych (lub Firebug), oszacowana wartość będzie wartością ostatniej instrukcji wyrażenia . W tym przypadku ostatnią instrukcją wyrażenia jest [true].

2) W kontekście przypisania parser upewni się, że {}jest to literał obiektu. Kiedy robisz var a = {}[true], usuwasz wszelkie niejednoznaczności i przechylasz parser, który {}nie jest instrukcją blokową.
Więc tutaj próbujesz uzyskać wartość za pomocą klucza "true"z pustego obiektu. Oczywiście z tą nazwą klucza nie ma pary klucz-wartość. W ten sposób zmienna a jest niezdefiniowana.

Zarezerwowane słowa jako klucze obiektów

ECMAScript 5 pozwala, aby klucze obiektów były słowami zastrzeżonymi. Tak więc następujące klucze są legalne:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3) To samo wyjaśnienie przykładu 1 . Ale ... Jeśli { b: 12345 }część jest traktowana jako instrukcja blokowa, jaki jest jej typ b: 12345?

... (?????)

To instrukcja etykiety , którą już widzieliście ... Jest używana w pętlach i w switch. Oto kilka interesujących linków dotyczących instrukcji etykiet: 1 , (2) [ Najlepszy sposób na przerwanie zagnieżdżonych pętli w JavaScript? , (3) [ Jak przerwać zagnieżdżone pętle w javascript? .

UWAGA: Po prostu spróbuj to ocenić:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

Instrukcje etykiet nie mogą być oddzielane przecinkiem , należy je oddzielić średnikiem. Więc to jest ważne:{a: 1; b: 2}

4) Zobacz objaśnienia do przykładów 1 i 3 ...

5) Jeszcze raz, mamy { b: 12345 }istnienie traktowane jako blok kodu, a ty próbujesz uzyskać dostęp do właściwości bloku kodu za pomocą notacji z kropką i oczywiście jest to niedozwolone, a parser zgłasza "Unexpected token :"wyjątek.

6) Kod jest prawie identyczny z powyższym przykładem, ale otaczając { b: 12345 }instrukcję operatorem grupowania wyrażeń , parser będzie wiedział, że jest to obiekt. W ten sposób będziesz mógł "b"normalnie uzyskać dostęp do nieruchomości.

7) Zapamiętaj przykład 2 , mamy tutaj przypisanie, parser wie, że { b: 12345 }jest to obiekt.

8) Identycznie jak w powyższym przykładzie, ale zamiast notacji z kropkami używamy tutaj notacji z nawiasami .

9) Powiedziałem już, że ta "identifier: value"składnia wewnątrz instrukcji blokowej jest etykietą. Ale musisz też wiedzieć, że nazwa etykiety nie może być zastrzeżonym słowem kluczowym (przeciwieństwo nazw właściwości obiektów). Kiedy próbowaliśmy zdefiniować etykietę o nazwie "true", otrzymaliśmy plik SyntaxError.

10) Ponownie mamy do czynienia z obiektem. Nie ma problemów ze słowami zastrzeżonymi. =)

11) Wreszcie mamy to:!{}[true]

Oddzielmy rzeczy tutaj:

a) Wykonując negację, informujemy parser, że {}jest obiektem .

b) Jak pokazano w przykładzie 2 , {}obiekt nie ma właściwości o nazwie true, więc to wyrażenie będzie obliczane do undefined.

c) Ostatecznym wynikiem jest negacja undefinedwartości. JavaScript wykonuje niejawną konwersję typu , a undefinedwartość jest fałszywa .

d) A więc negacja falsejest ... true!

Alcides Queiroz Aguiar
źródło