JavaScriptowe WTF, czyli co jest wynikiem [] + [], [] + {}, {} + [] itp.?

Ostatnio obejrzałem ciekawą prezentację dotyczącą tzw. watthefucków w języku JavaScript (i Rubym, ale jego pominę ponieważ poza prostą aplikacją typu CRUD na studiach mam minimalne doświadczenie w pracy z tym językiem).

Prezentacja jest zabawna, okraszona śmiesznymi obrazkami i na pierwszy rzut oka faktycznie początkujący mogą zdziwić się o co chodzi? Sam nie jestem wymiataczem w JS, ale po wykonaniu małego researchu* zrozumiałem, że pokazane operacje nie są zupełnie bez sensu, a nawet jest w nich pewna logika :P

1. Dodawanie tablic [] + []

Specyfikiacja JS mówi, że operator dodawania służy do dodawania liczb lub konkatenacji stringów. Jeśli operator dodawania wywołany jest dla innych typów, muszą one być najpierw przekonwertowane do typu prostego – łańcucha lub liczby, które można dodać. Wywołanie na pustej tablicy metody toString(), powoduje skonwertowanie jej do typu prostego – pustego łańcucha. Zatem dodanie dwóch pustych łańuchów w rezultacie daje także pusty łańcuch.

2. Dodawanie tablicy i obiektu [] + {}

Podobnie jak wyżej – najpierw następuje konwersja do typów prostych. W jej rezultacie dodawane są dwa łańcuchy: "" + "[object Object]". Wynikiem nie jest zatem obiekt, jak twierdzi prowadzący prezentację, a łańcuch o zawartości "[object Object]".

3. Dodawanie obiektu do tablicy {} + []

{} to w tym przypadku nie tworzenie nowego pustego obiektu, a blok instrukcji. Decydują o tym priorytety operatorów.* Blok obejmuje instrukcje, występuje np. w warunkach if, czy definicjach funkcji.

W tym przypadku mamy pusty blok, bez instrukcji, który w zasadzie jest pomijany. Zostaje zatem +[], czyli tablica poprzedzona jednoargumentowym operatorem dodawania. Operator ten próbuje przekształcić tablicę na liczbę. Pusta tablica daje nam w wyniku takiej konwersji 0.

*) Nagłówek tego punktu jest zatem błędny – gdybyśmy chcieli faktycznie rozpocząć tę instrukcję od tworzenia pustego obiektu, nawiasy klamrowe należałoby otoczyć nawiasem okrągłym – ({}).

4. Dodawanie obiektu do obiektu {} + {}

Pierwsza para nawiasów {}, podobnie jak wyżej, jest pomijana jako pusty blok, pozostaje zatem +{}, czyli jednoargumentowy operator dodawania, próbujący przekształcić obiekt na liczbę. Z pustego obiektu nie da się jednak „wyciągnąć” żadnej liczby, stąd zwracane jest NaN.

5. Tworzenie nowej tablicy Array(16)

W tym przypadku prowadzący się pomylił – przecinków nie jest 16, tylko 15. Oddzielają one niezdefiniowane elementy tablicy.

6. Array(16).join("wat")

W uproszczeniu, funkcja join() łączy wszystkie elementy tablicy w jeden łańcuch, zastępując przecinki oddzielające elementy tablicy argumentem funkcji. W naszym przypadku jest 16 niezdefiniowanych elementów oddzielonych 15 przecinkami. W rezultacie otrzymujemy zatem 15 powtórzeń słowa "wat".

7. Array(16).join("wat" + 1)

Przypadek jak wyżej, z tą różnicą, że 1 jest rzutowane na typ string, w efekcie czego jako argument funkcji join() poodane jest "wat1".

8. Array(16).join("wat" - 1)

Operator odejmowania moze być stosowany dla typów numerycznych. Dla innego typu najpierw następuje próba rzutowania. ToNumber("wat") zwraca NaN, w efekcie mamy zatem powótrzenie NaN 15 razy.

9. Watman!

Watman powstaje po dodaniu do ostatniego polecenia +"Batman" ;-)

Podsumowanie

Na koniec, aby jeszcze lepiej zrozumieć powyższe zawiłości polecam samemu wydać w konsoli polecenia:

  • {} + "wat"
  • {} + [1]
  • ({} + [1])
  • ({} + [1,2])
  • +{}
  • {} + []
I tym podobne kombinacje, a następnie sprawdzić wynik ich działania operatorem typof().
Natomiast gdyby komuś było mało, „wyższy poziom abstrakcji” JavaScriptowej można sprawdzić pod poniższymi adresami:
*) Źródła:

SKOMENTUJ