Dwa rodzaje koercji
JavaScript luźno podchodzi do typów. Gdy operator dostaje wartość „złego” typu, język nie rzuca błędu, tylko ją konwertuje. Taka konwersja to koercja (coercion) i występuje w dwóch odmianach:
- Jawna: sam o nią prosisz,
Number("42"),String(99),Boolean(value). - Niejawna: uruchamia ją operator, choć nie napisałeś konwersji,
"5" - 2,"" == 0,if (value).
Obie ostatecznie korzystają z tych samych reguł konwersji. Różnica polega na tym, czy o konwersji decydujesz ty, czy język. Niemal każdy mylący wynik w JavaScript, "5" + 1 === "51", [] == false, null == undefined, bierze się z niejawnej koercji, która zaskakuje cię znienacka.
Model myślowy: jeśli piszesz Number(...) albo String(...), wiesz, o co prosisz. Jeśli nie, każdy operator ma własne zdanie na temat tego, co zrobić, i właśnie w tych zdaniach mieszkają błędy.
Trzy typy docelowe
Koercja zawsze konwertuje do jednego z trzech typów prymitywnych: string, number albo boolean. (Jest też czwarty, bigint, ale inne typy nigdy nie są do niego automatycznie konwertowane). Cała reszta reguł wynika z tego, do którego typu dąży dany operator.
Koercja do stringa jest najbardziej wyrozumiała: każda wartość ma postać tekstową. Zauważ, że obiekty zamieniają się na mało pomocne "[object Object]", dlatego logowanie obiektu doklejonego do stringa ("user: " + user) prawie nigdy nie pokazuje tego, czego chcesz. Zamiast tego użyj JSON.stringify albo template literals z konkretnymi polami.
Koercja do liczby
Koercja do liczby jest bardziej rygorystyczna. Stringi muszą naprawdę wyglądać jak liczba, w przeciwnym razie dostajesz NaN:
Niespodzianki oznaczone w nawiasach warto zapamiętać:
- Pusty string i string z samych spacji dają
0, a nieNaN. nulldaje0, aleundefineddajeNaN.- Pusta tablica daje
0, tablica z jednym elementem jest konwertowana jak ten element, a tablica z wieloma elementami dajeNaN.
Jeśli chcesz wyciągnąć liczbę ze stringa z dodatkowymi znakami, użyj parseInt albo parseFloat zamiast Number:
Zawsze przekazuj do parseInt podstawę systemu (10). Bez niej stringi zaczynające się od "0x" są parsowane szesnastkowo, a rzadko o to ci chodzi.
Koercja do boolean
Koercja do boolean jest najprostsza z trzech. Krótka lista wartości zamienia się na false, a wszystko inne na true.
Wartości falsy to:
false0,-0,0n""(pusty string)nullundefinedNaN
To wszystko. Każda inna wartość, w tym "false", "0", [] i {}, jest truthy.
Wyniki dla pustej tablicy i pustego obiektu zaskakują osoby przychodzące z Pythona, gdzie puste kolekcje są falsy. W JavaScript są truthy: jeśli chcesz sprawdzić, czy tablica jest pusta, sprawdź jawnie arr.length === 0.
Koercja do boolean zachodzi zawsze, gdy wartość pojawia się tam, gdzie oczekiwany jest boolean: if (...), while (...), operator trójargumentowy ? : oraz operatory logiczne &&, ||, !.
Operator + to przypadek szczególny
Większość operatorów arytmetycznych wymusza zamianę operandów na liczby. + działa inaczej: jeśli którakolwiek strona jest stringiem, + łączy stringi. W przeciwnym razie dodaje liczby.
Obliczanie przebiega od lewej do prawej. 1 + 2 + "3" najpierw liczy 1 + 2 = 3, a potem 3 + "3" = "33". Natomiast "1" + 2 + 3 od początku jest stringiem i już nim pozostaje: "12", potem "123".
Dlatego budowanie stringów przez + jest kruche. Template literals nie mają tego problemu:
Template literal oblicza count + 1 jako osobne wyrażenie liczbowe, a dopiero potem wstawia wynik. Żadnej niespodziewanej koercji.
Wybieraj jawne konwersje
Gdy musisz zmienić typ, powiedz to wprost. Kosztuje to kilka znaków, a następnej osobie czytającej kod usuwa wszelkie wątpliwości:
Podobnie z wartościami logicznymi. !!value działa i jest popularne, ale Boolean(value) mówi dokładnie, co robi:
Rozsądna zasada: w logice aplikacji używaj jawnych konwersji, a krótkie formy (+x, !!x) zostaw na miejsca, gdzie liczy się zwięzłość, a intencja jest oczywista z kontekstu.
Operator == mocno polega na koercji
Operatory równości to największe źródło niespodzianek związanych z koercją. == konwertuje przed porównaniem, === nie.
Każde true powyżej to wynik wieloetapowego łańcucha koercji, którego większość programistów nie wyrecytuje z pamięci. I w tym problem: kod, który działa przypadkiem, to kod, który później się zepsuje. Pełne reguły równości omówimy na następnej stronie. Na razie zapamiętaj: domyślnie używaj ===, a po == sięgaj tylko w konkretnym idiomie x == null (który łapie zarówno null, jak i undefined).
Wszystko razem
Przykład pokazujący, gdzie koercja pomaga, a gdzie szkodzi:
Zwróć uwagę na drugie wywołanie. Number("") zwraca 0, a nie NaN, więc parsePrice("") zwraca 0, czego użytkownik tej funkcji raczej by nie chciał. Jeśli puste dane mają być odrzucane, dodaj jawne sprawdzenie:
Wiedza, które wartości zamieniają się na 0, a które na NaN, to właśnie to, co chroni cię przed subtelnym błędem w dalszej części kodu.
Co warto zapamiętać
- Koercja konwertuje do string, number albo boolean, zależnie od operatora.
+z jakimkolwiek stringiem staje się łączeniem stringów, a każdy inny operator arytmetyczny konwertuje do liczby.- Wartości falsy to stała, krótka lista: zapamiętaj ją. Wszystko inne jest truthy, w tym
[]i{}. Number("")to0,Number([])to0,Number(null)to0, aleNumber(undefined)toNaN. To pojawia się w prawdziwych błędach.- Wybieraj
Number(x),String(x),Boolean(x)zamiast sprytnych niejawnych sztuczek. Przyszły ty będzie ci wdzięczny.
Dalej: operatory równości
Wszystko, co koercja robi w porównaniach, dzieje się wewnątrz ==. Następna strona omawia ==, === i Object.is, jedyny idiom, w którym == nadal się przydaje, i powód, dla którego lintery domyślnie oznaczają luźną formę.
Najczęściej zadawane pytania
Czym jest koercja typów w JavaScript?
Koercja typów (type coercion) to automatyczna zamiana wartości z jednego typu na inny: liczby w string, stringa w liczbę albo czegokolwiek w boolean. Dzieje się niejawnie, gdy operatory takie jak +, == czy if dostają wartość innego typu, niż oczekują, i jawnie, gdy sam wywołujesz Number(x), String(x) albo Boolean(x).
Czym różni się koercja niejawna od jawnej?
Koercja jawna to celowe wywołanie funkcji konwertującej: Number("42"), String(99), Boolean(value). Koercja niejawna to konwersja uruchamiana przez operator za twoimi plecami: "5" - 2 daje 3, a "5" + 2 daje "52". Jawna jest czytelna i przewidywalna, niejawna stoi za większością błędów typu „skąd tu NaN”.
Jak zamienić string na liczbę w JavaScript?
Użyj Number("42") do ścisłej konwersji (zwraca NaN, jeśli string nie jest poprawną liczbą) albo parseInt("42px", 10) / parseFloat("3.14em"), jeśli chcesz odczytać liczbę z początku mniej uporządkowanego stringa. Jednoargumentowy operator + (+"42") działa tak samo jak Number(), ale łatwiej go przeoczyć przy przeglądaniu kodu.
Dlaczego [] + [] zwraca pusty string?
[] + [] zwraca pusty string?Operator + nie ma sensownej operacji liczbowej dla dwóch tablic, więc JavaScript zamienia obie na stringi. Tablice zamieniają się na string przez połączenie elementów przecinkami, a pusta tablica daje "". Tak więc [] + [] staje się "" + "", czyli "". To zabawna sztuczka i dobry powód, żeby nigdy nie polegać na + z wartościami nieprymitywnymi.