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
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])
+{}
{} + []
typof()
.