Dlaczego ++[[]][+[]]+[+[]] zwraca ciąg "10"?

Jest to poprawne i zwraca łańcuch "10" w JavaScript ( więcej przykładów tutaj):

console.log(++[[]][+[]]+[+[]])
Dlaczego? Co tu się dzieje?
Author: SapuSeven, 2011-08-26

8 answers

Jeśli go podzielimy, bałagan jest równy:

++[[]][+[]]
+
[+[]]

W JavaScript prawdą jest, że +[] === 0. + konwertuje coś na liczbę i w tym przypadku sprowadza się do +"" lub 0 (szczegóły specyfikacji poniżej).

Dlatego możemy ją uprościć (++ ma precendencję nad +):

++[[]][0]
+
[0]

Ponieważ [[]][0] oznacza: get the first element from [[]], to prawda, że:

  • [[]][0] zwraca wewnętrzną tablicę ([]). Ze względu na referencje jest źle powiedzmy [[]][0] === [], ale nazwijmy tablicę wewnętrzną A, aby uniknąć niewłaściwej notacji.
  • ++[[]][0] == A + 1, ponieważ ++ oznacza "przyrost o jeden".
  • ++[[]][0] === +(A + 1); innymi słowy, zawsze będzie to liczba (+1 niekoniecznie Zwraca liczbę, podczas gdy ++ zawsze to robi - dzięki Timowi za wskazanie tego).

Ponownie, możemy uprościć bałagan do czegoś bardziej czytelnego. Zastąp [] z powrotem na A:

+([] + 1)
+
[0]

W JavaScript jest to prawda jako cóż: [] + 1 === "1", ponieważ [] == "" (łączenie pustej tablicy), więc:

  • +([] + 1) === +("" + 1), oraz
  • +("" + 1) === +("1"), oraz
  • +("1") === 1

Uprośćmy to jeszcze bardziej:

1
+
[0]

Jest to również prawdą w JavaScript: [0] == "0", ponieważ łączy tablicę z jednym elementem. Połączenie połączy elementy oddzielone przez ,. Z jednym elementem można wywnioskować, że ta logika spowoduje sam pierwszy element.

Tak więc w końcu otrzymujemy (liczba + ciąg = string):

1
+
"0"

=== "10" // Yay!

Szczegóły specyfikacji dla +[]:

Jest to dość labirynt, ale do zrobienia +[], najpierw jest konwertowany na ciąg znaków, ponieważ tak mówi +: {]}

11.4.6 Unary + Operator

Operator uniary + przekształca swój operand na typ liczby.

Jednostkowa ekspresja produkcji : + jednostkowa ekspresja jest oceniana w następujący sposób:

  1. Niech expr będzie wynikiem oceny UnaryExpression.

  2. Return ToNumber(GetValue(expr)).

ToNumber() mówi:

Obiekt

Zastosuj następujące kroki:

  1. Niech primValue będzie ToPrimitive (argument wejściowy, Łańcuch podpowiedzi).

  2. Return ToString (primValue).

ToPrimitive() mówi:

Obiekt

Zwraca wartość domyślną dla obiektu. Wartość domyślna obiekt jest pobierany przez wywołanie wewnętrznej metody [[DefaultValue]] obiektu, przekazując opcjonalną podpowiedź PreferredType. Zachowanie wewnętrznej metody [[DefaultValue]] jest zdefiniowane w niniejszej specyfikacji dla wszystkich natywnych obiektów ECMAScript w wersji 8.12.8.

[[DefaultValue]] mówi:

8.12.8 [[DefaultValue]] (hint)

Gdy wywołana jest wewnętrzna metoda [[DefaultValue]] o z łańcuchem podpowiedzi, wykonywane są następujące kroki:

  1. Niech ToString będzie wynikiem wywołania wewnętrznej metody [[Get]] obiektu o z argumentem "ToString".

  2. Jeśli IsCallable (toString) jest prawdziwe, to

A. niech str będzie wynikiem wywołania wewnętrznej metody ToString [[Call]], Z O jako tą wartością i pustą listą argumentów.

B. Jeśli str jest wartością pierwotną, zwróć str.

.toString tablicy mówi:

15.4.4.2 / Align = "left" / prototyp.toString ()

Po wywołaniu metody toString podejmowane są następujące kroki:

  1. Niech tablica będzie wynikiem wywołania ToObject na tej wartości.

  2. Niech func będzie wynikiem wywołania wewnętrznej metody [[Get]] tablicy z argumentem "join".

  3. Jeżeli IsCallable (func) jest false, to niech func będzie standardowym obiektem wbudowanym metody.prototyp.toString (15.2.4.2).

  4. Return { wynik wywołania wewnętrznej metody [[Call]] func dostarczającej tablicę jako tą wartość i pustą listę argumentów.

Więc +[] sprowadza się do +"", ponieważ [].join() === "".

Ponownie, + jest zdefiniowany jako:

11.4.6 Unary + Operator

Operator uniary + przekształca swój operand na typ liczby.

Jednostkowa ekspresja produkcji : + jednostkowa ekspresja jest oceniana w następujący sposób:

  1. Niech expr być wynikiem oceny jednokierunkowej ekspresji.

  2. Return ToNumber(GetValue(expr)).

ToNumber jest zdefiniowana dla "" jako:

MV StringNumericLiteral::: [empty] wynosi 0.

Więc +"" === 0, a więc +[] === 0.

 1904
Author: pimvdb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 21:13:44
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Wtedy mamy konkatenację ciągu

1+[0].toString() = 10
 104
Author: Shef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-26 08:58:23

Poniższy wpis jest zaadaptowany z blogu odpowiadając na to pytanie, które napisałem, gdy to pytanie było jeszcze zamknięte. Odsyłacze są (kopią HTML) specyfikacji ECMAScript 3, wciąż podstawowej dla JavaScript w dzisiejszych powszechnie używanych przeglądarkach internetowych.

Po pierwsze, komentarz: tego rodzaju wyrażenie nigdy nie pojawi się w żadnym (zdrowym) środowisku produkcyjnym i jest użyte tylko jako ćwiczenie, jak dobrze czytelnik zna brudne krawędzie JavaScript. Generał zasada, że operatory JavaScript domyślnie konwertują między typami, jest użyteczna, podobnie jak niektóre popularne konwersje, ale wiele szczegółów w tym przypadku nie jest.

Wyrażenie ++[[]][+[]]+[+[]] może początkowo wyglądać dość imponująco i niejasno, ale w rzeczywistości jest stosunkowo łatwe do rozbicia 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 zweryfikować, możesz poczytać o grupowaniu operator . Tak więc wyrażenie może być wyraźniej zapisane jako

( ++[[]][+[]] ) + ( [+[]] )

Rozkładając to, możemy uprościć obserwując, że +[] ocenia do 0. Aby upewnić się, dlaczego jest to prawdą, sprawdź operator unary + i podążaj lekko krętą ścieżką, która kończy się ToPrimitive przekształcając pustą tablicę w pusty łańcuch, który jest ostatecznie zamieniany na 0przez ToNumber. Możemy teraz zastąpić 0 dla każdej instancji +[]:

( ++[[]][0] ) + [0]

Już prostsze. Jeśli chodzi o ++[[]][0], jest to kombinacja operatora przyrostka (++), W przypadku, gdy nie jest to możliwe, nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe, ponieważ nie jest to możliwe. ([0]) wywoływana na tablicy zdefiniowanej przez literał tablicy.

Więc możemy uprościć [[]][0] do po prostu [] i mamy ++[], prawda? W rzeczywistości tak nie jest, ponieważ ocena ++[] rzuca błąd, który może początkowo wydawać się mylące. Jednak trochę myśli o naturze ++ jasno to wyjaśnia: służy do zwiększania zmiennej (np. ++i) lub właściwości obiektu (np. ++obj.count). Nie tylko ocenia do wartości, ale także przechowuje tę wartość gdzieś. W przypadku ++[], nie ma gdzie umieścić nowej wartości (cokolwiek to może być), ponieważ nie ma odniesienia do Właściwości obiektu lub zmiennej do aktualizacji. W terminach spec jest to objęte wewnętrzną operacją PutValue , który jest wywoływany przez operator przyrostu prefiksu.

Więc co robi ++[[]][0]? Cóż, według podobnej logiki jak +[], wewnętrzna tablica jest konwertowana na 0 i ta wartość jest zwiększana o 1, aby dać nam ostateczną wartość 1. Wartość właściwości 0 w zewnętrznej tablicy jest aktualizowana do 1, a całe wyrażenie jest ewaluowane do 1.

To pozostawia nam

1 + [0]

... co jest prostym zastosowaniem operatora dodawania . Oba operandy są pierwsze skonwertowane na primitives i jeśli któraś z wartości primitives jest łańcuchem znaków, to jest wykonywana konkatenacja łańcuchów, w przeciwnym razie wykonywane jest dodawanie liczb. [0] konwertuje na "0", więc używa się konkatenacji łańcuchów, tworząc "10".

Na marginesie, coś, co może nie być od razu oczywiste, jest to, że nadpisanie jednej z toString() lub valueOf() metod Array.prototype zmieni wynik wyrażenia, ponieważ obie są sprawdzane i używane, jeśli są obecne podczas konwersji obiektu w prymitywną wartość. Na przykład, następujące

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

... produkuje "NaNfoo". Dlaczego tak się dzieje, pozostaje jako ćwiczenie dla czytelnika...

 57
Author: Tim Down,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-29 19:00:08

Zróbmy to prosto:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"
 21
Author: renatoluna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-07 12:16:52

Ten ocenia na ten sam, ale nieco mniejszy

+!![]+''+(+[])
  • [] - jest konwertowana tablica, która jest konwertowana na 0 po dodaniu lub odjęciu z niej, więc stąd + [] = 0
  • ![]- ocenia NA false, więc stąd !![] evaluates to true
  • +!![]- zamienia wartość true na wartość liczbową, która zwraca wartość true, więc w tym przypadku 1
  • +" - doda pusty łańcuch do wyrażenia powodujący, że liczba zostanie przekonwertowana na łańcuch
  • + [] - ocenia do 0

Więc jest oceniana na

+(true) + '' + (0)
1 + '' + 0
"10"

Więc teraz masz to, spróbuj tego:

_=$=+[],++_+''+$
 13
Author: Vlad Shlosberg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-19 16:54:24

+ [] ocenia do 0 [...] następnie sumowanie (operacja+) z czymkolwiek konwertuje zawartość tablicy na jej reprezentację łańcuchową składającą się z elementów połączonych przecinkiem.

Wszystko inne jak pobieranie indeksu tablicy (mają priorytet gratera niż operacja+) jest porządkowe i nic ciekawego.

 7
Author: Eskat0n,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-26 08:58:44

Być może najkrótsze możliwe sposoby oceny wyrażenia na "10" bez cyfr to:

+!+[] + [+[]] // "10"

-~[] + [+[]] // "10"

//========== Wyjaśnienie ==========\\

+!+[] : +[] konwertuje na 0. !0 konwertuje na true. +true konwertuje do 1. -~[] = -(-1) czyli 1

[+[]] : +[] konwertuje na 0. [0] jest tablicą z pojedynczym elementem 0.

Następnie js ocenia 1 + [0], a więc Number + Array wyrażenie. Następnie ECMA Specyfikacja działa: operator + konwertuje oba operandy na łańcuch znaków, wywołując funkcje toString()/valueOf() z prototypu bazowego Object. Działa jako funkcja addytywna, jeśli oba operandy wyrażenia są tylko liczbami. Sztuczka polega na tym, że tablice łatwo przekształcają swoje elementy w skonkatenowaną reprezentację łańcuchową.

Niektóre przykłady:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

Jest miły wyjątek, że dwa Objects dodawanie powoduje NaN:

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"
 4
Author: AsyncMind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-19 12:19:35
  1. Unary plus podany łańcuch przekształca się na liczbę
  2. operator inkrementacji podany łańcuch przekształca i inkrementuje o 1
  3. [] == ''. Empty String
  4. + " lub + [] ocenia 0.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    
 1
Author: Praveen Vedanth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-30 15:53:30