Jest to poprawne i zwraca ciąg znaków "10"
w JavaScript ( więcej przykładów tutaj ):
console.log(++[[]][+[]]+[+[]])
Dlaczego? Co tu się dzieje?
javascript
syntax
SapuSeven
źródło
źródło
+[]
rzuca pustą tablicę na0
... a potemOdpowiedzi:
Jeśli podzielimy to, bałagan będzie równy:
W JavaScript jest to prawda
+[] === 0
.+
konwertuje coś na liczbę, w tym przypadku sprowadza się do+""
lub0
(patrz szczegóły specyfikacji poniżej).Dlatego możemy to uprościć (
++
ma przewagę nad+
):Ponieważ
[[]][0]
oznacza: pobierz pierwszy element[[]]
, to prawda, że:[[]][0]
zwraca tablicę wewnętrzną ([]
). Z powodu odniesień nie można powiedzieć[[]][0] === []
, ale wywołajmy tablicę wewnętrzną,A
aby uniknąć niewłaściwej notacji.++
przed operandem oznacza „przyrost o jeden i zwrócenie przyrostu wyniku”.++[[]][0]
Jest więc równoważne zNumber(A) + 1
(lub+A + 1
).Ponownie możemy uprościć bałagan w coś bardziej czytelnego. Zastąpmy z
[]
powrotem zaA
:Zanim będzie
+[]
można zmusić tablicę do liczby0
, najpierw trzeba ją przymusić do łańcucha, czyli""
znowu. Wreszcie1
dodaje się, co powoduje1
.(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Uprośćmy to jeszcze bardziej:
Jest to również prawdą w JavaScript:
[0] == "0"
ponieważ łączy tablicę z jednym elementem. Łączenie połączy elementy oddzielone,
. Za pomocą jednego elementu można wywnioskować, że ta logika spowoduje powstanie samego pierwszego elementu.W tym przypadku
+
widzi dwa operandy: liczbę i tablicę. Teraz próbuje zmusić oba do tego samego typu. Najpierw tablica jest wymuszana na ciąg"0"
, następnie liczba jest wymuszana na ciąg ("1"
). Liczba+
Ciąg===
Ciąg .Szczegóły specyfikacji dla
+[]
:Jest to dość labirynt, ale aby to zrobić
+[]
, najpierw jest konwertowany na ciąg, ponieważ tak+
mówi:ToNumber()
mówi:ToPrimitive()
mówi:[[DefaultValue]]
mówi:.toString
Tablicy mówi:Tak
+[]
sprowadza się do tego+""
, ponieważ[].join() === ""
.Ponownie,
+
jest to zdefiniowane jako:ToNumber
jest zdefiniowany""
jako:A więc
+"" === 0
i tak+[] === 0
.źródło
true
jeśli zarówno wartość, jak i typ są takie same.0 == ""
zwracatrue
(to samo po konwersji typu), ale0 === ""
jestfalse
(nie tych samych typów).1 + [0]
, nie"1" + [0]
, ponieważ++
operator prefiksu ( ) zawsze zwraca liczbę. Patrz bclary.com/2004/11/07/#a-11.4.4++[[]][0]
zwraca rzeczywiście1
, ale++[]
generuje błąd. Jest to niezwykłe, ponieważ wygląda na to, że++[[]][0]
sprowadza się do++[]
. Czy może masz pojęcie, dlaczego++[]
zgłasza błąd, a++[[]][0]
nie robi?PutValue
wywołaniu (w terminologii ES3, 8.7.2) w operacji prefiksu.PutValue
wymaga odwołania, podczas gdy[]
samo wyrażenie nie tworzy odniesienia. Wyrażenie zawierające zmienne odwołanie (powiedzmy, że wcześniej zdefiniowaliśmy,var a = []
a następnie++a
działa) lub dostęp do właściwości obiektu (np.[[]][0]
) Tworzy odwołanie. Mówiąc prościej, operator prefiksu nie tylko tworzy wartość, ale także musi ją gdzieś umieścić.var a = []; ++a
,a
to 1. Po wykonaniu++[[]][0]
tablica tworzona przez[[]]
wyrażenie jest teraz zawiera tylko numer 1 w indeksie 0.++
wymaga Reference to zrobić.Następnie mamy konkatenację strun
źródło
===
niż=>
?Poniższy fragment został zaadaptowany z postu na blogu, w którym odpowiedziałem na to pytanie, które opublikowałem, gdy to pytanie było jeszcze zamknięte. Odsyłacze są (kopią HTML) specyfikacji ECMAScript 3, będącej nadal podstawą dla JavaScript w dzisiejszych powszechnie używanych przeglądarkach internetowych.
Po pierwsze, komentarz: ten rodzaj wyrażenia nigdy nie pojawi się w żadnym (rozsądnym) środowisku produkcyjnym i służy jedynie jako ćwiczenie tego, jak dobrze czytelnik zna brudne krawędzie JavaScript. Ogólna zasada, że operatorzy JavaScript domyślnie dokonują konwersji między typami, jest użyteczna, podobnie jak niektóre typowe konwersje, ale większość szczegółów w tym przypadku nie jest.
Wyrażenie
++[[]][+[]]+[+[]]
może początkowo wyglądać narzucająco i niejasno, ale w rzeczywistości jest stosunkowo łatwe do podziału na osobne wyrażenia. Poniżej po prostu dodałem nawiasy dla jasności; Zapewniam cię, że nic nie zmieniają, ale jeśli chcesz to sprawdzić, możesz przeczytać o operatorze grupowania . Wyrażenie można więc wyraźniej zapisać jakoRozbijając to, możemy uprościć, obserwując, który
+[]
ocenia0
. Aby zadowolić siebie, dlaczego tak jest, sprawdź operator jednoargumentowy + , a następnie lekko kręty szlak, który kończy się ToPrimitive przekształcenie pustą tablicę na pusty ciąg, który następnie przekształca się w końcu0
przez ToNumber . Możemy teraz zastąpić0
każdą instancję+[]
:Już prościej. Jeśli chodzi o to
++[[]][0]
, jest to kombinacja operatora przyrostu prefiksu (++
), literału tablicowego definiującego tablicę z pojedynczym elementem, który sam jest pustą tablicą ([[]]
) i accessorem właściwości ([0]
) wywoływanym na tablicę zdefiniowaną przez literał tablicowy.Tak więc, możemy uprościć
[[]][0]
po prostu[]
i mamy++[]
, prawda? W rzeczywistości tak nie jest, ponieważ ocena++[]
powoduje błąd, który początkowo może wydawać się mylący. Jednak mała refleksja na temat natury++
wyjaśnia: jest używana do zwiększania zmiennej (np.++i
) Lub właściwości obiektu (np++obj.count
.). Nie tylko ocenia wartość, ale także gdzieś ją zapisuje. W przypadku++[]
, nie ma gdzie umieścić nowej wartości (cokolwiek by to nie było), ponieważ nie ma odniesienia do właściwości obiektu lub zmiennej do aktualizacji. Mówiąc konkretnie , jest to objęte wewnętrzną operacją PutValue , która jest wywoływana przez operator inkrementacji prefiksu.Co więc robi
++[[]][0]
? Cóż, według podobnej logiki+[]
, tablica wewnętrzna jest konwertowana na,0
a wartość ta jest zwiększana o,1
aby dać nam końcową wartość1
. Wartość właściwości0
w zewnętrznej tablicy jest aktualizowana do,1
a całe wyrażenie ocenia na1
.To nas zostawia
... co jest prostym zastosowaniem operatora dodawania . Oba operandy są najpierw konwertowane na prymitywy, a jeśli jedna z wartości pierwotnych jest łańcuchem, wykonywana jest konkatenacja łańcucha, w przeciwnym razie przeprowadzane jest dodawanie liczbowe.
[0]
konwertuje na"0"
, więc używana jest konkatenacja łańcuchów, tworząc"10"
.Na koniec, czymś, co może nie być od razu widoczne, jest to, że przesłonięcie jednej z metod
toString()
lubvalueOf()
metodyArray.prototype
zmieni wynik wyrażenia, ponieważ obie są sprawdzane i używane, jeśli występują podczas przekształcania obiektu w pierwotną wartość. Na przykład następujące... produkuje
"NaNfoo"
. Dlaczego tak się dzieje, pozostaje zadaniem czytelnika ...źródło
Uprośćmy to:
źródło
Ten ocenia to samo, ale nieco mniej
tak ocenia
Więc teraz masz to, spróbuj tego:
źródło
"10"
+ [] ocenia na 0 [...], a następnie sumuje (operacja +) z czymkolwiek konwertuje zawartość tablicy na reprezentację ciągu składającą się z elementów połączonych przecinkiem.
Cokolwiek innego, jak pobranie indeksu tablicy (ma większy priorytet niż operacja +), jest porządkowe i nie jest niczym interesującym.
źródło
Być może najkrótszymi możliwymi sposobami oceny wyrażenia na „10” bez cyfr są:
+!+[] + [+[]]
// „10”-~[] + [+[]]
// „10”// ========== Objaśnienie ========== \\
+!+[]
:+[]
Konwertuje na 0.!0
konwertuje natrue
.+true
konwertuje na 1.-~[]
=-(-1)
co jest 1[+[]]
:+[]
Konwertuje na 0.[0]
to tablica z pojedynczym elementem 0.Następnie JS ocenia
1 + [0]
, zatemNumber + Array
wyrażenie. Następnie działa specyfikacja ECMA:+
operator konwertuje oba operandy na ciąg, wywołująctoString()/valueOf()
funkcje z bazyObject
prototypu. Działa jako funkcja addytywna, jeśli oba operandy wyrażenia są tylko liczbami. Sztuka polega na tym, że tablice łatwo przekształcają swoje elementy w połączoną reprezentację łańcucha.Kilka przykładów:
Jest ładny wyjątek, który
Objects
powoduje dwa dodaniaNaN
:źródło
+ '' lub + [] ocenia 0.
źródło
[]
jest równoważne z . Najpierw element jest wyodrębniany, a następnie konwertowany przez .""
++
Krok po kroku
+
zamień wartość na liczbę, a jeśli dodasz do pustej tablicy+[]
... ponieważ jest pusta i równa się0
, będzieStamtąd, spójrz teraz na swój kod, to jest
++[[]][+[]]+[+[]]
...I jest między nimi
++[[]][+[]]
plus[+[]]
Tak więc
[+[]]
powrócą,[0]
ponieważ mają pustą tablicę, na którą można przekonwertować0
wewnątrz drugiej tablicy ...Jak więc sobie wyobrazić, pierwszą wartością jest dwuwymiarowa tablica z jedną tablicą wewnątrz ... więc
[[]][+[]]
będzie równa,[[]][0]
która zwróci[]
...Na koniec
++
przekonwertuj go i zwiększ do1
...Więc możesz sobie wyobrazić,
1
+"0"
będzie"10"
...źródło