Siedem prymitywów i cała reszta
JavaScript dzieli wartości na dwa obozy. Po jednej stronie jest siedem typów prymitywnych: prostych, niemutowalnych wartości. Po drugiej stronie są obiekty: wszystko, co złożone, mutowalne albo wywoływalne. Na poziomie wartości to cały system typów.
Siedem prymitywów:
Wszystko, czego nie ma na tej liście (tablice, funkcje, daty, wyrażenia regularne, zwykłe {}), jest obiektem. typeof podaje typ w czasie działania programu, a w ostatniej linii widać słynną wadę języka. typeof null zwraca 'object' od 1995 roku i nigdy nie zostanie poprawione, bo zbyt dużo istniejącego kodu na tym polega.
Prymityw to wartość, a nie pojemnik
Najbardziej pomocny model myślowy: prymityw jest swoją wartością. Liczba 3 to nie pudełko z trójką w środku, to po prostu 3. Dwie zmienne przechowujące 3 przechowują tę samą wartość, a nie dwie kopie wskazujące na coś wspólnego:
Prymitywy porównuje się przez wartość. Obiekty porównuje się przez referencję. To jedno rozróżnienie stoi za wieloma późniejszymi momentami typu „czemu to daje false?”, zwłaszcza przy porównywaniu tablic lub obiektów przez ===.
Prymitywy są niemutowalne
Prymitywu nie da się zmodyfikować. Każda operacja, która wygląda na zmianę, w rzeczywistości tworzy nową wartość:
Pierwsze wywołanie tworzy nowy string i go wyrzuca, bo nic nie przechwyciło zwróconej wartości. Drugie przypisuje name na nowo. Oryginalne "ada" nigdy się nie zmieniło i nie mogło się zmienić. Tak samo jest z liczbami: x + 1 tworzy nową liczbę, a nie zmienia x.
Dlatego const przy stringu lub liczbie jest naprawdę bezpieczne. Wartość nie może się zmienić, a const nie pozwala przypisać zmiennej na nowo.
Number, BigInt i dlaczego są dwa
number w JavaScript to 64-bitowa liczba zmiennoprzecinkowa. Oznacza to szybką arytmetykę i sufit: liczby całkowite są dokładne tylko do Number.MAX_SAFE_INTEGER (2^53 - 1):
Powyżej tego progu liczby całkowite zaczynają na siebie nachodzić. bigint istnieje dla liczb całkowitych, które mają pozostać dokładne przy dowolnej wielkości. Zapisujesz go, dodając na końcu n:
bigint i number nie mieszają się w arytmetyce, bo straciłbyś cały sens dodatkowej precyzji. Sięgaj po bigint przy identyfikatorach z bazy danych, znacznikach czasu w nanosekundach albo kryptografii. Zwykła arytmetyka zostaje przy number.
Stringi też są prymitywami
String w JavaScript to prymityw, a nie obiekt, chociaż udostępnia metody i właściwości takie jak .length, .slice, .toUpperCase:
Pod spodem, gdy wywołujesz metodę na stringu, JavaScript na chwilę opakowuje go w obiekt String, żeby wywołanie zadziałało, a potem wyrzuca to opakowanie. Nie musisz o nim myśleć. Wystarczy wiedzieć, że stringi zachowują się jak wartości (są niemutowalne i porównywane przez wartość), mimo że mają bogaty zestaw metod.
Apostrofy, cudzysłowy i backticki tworzą ten sam typ. Backticki dodatkowo pozwalają na interpolację i stringi wieloliniowe, o których jest następny artykuł.
null a undefined
Dwa prymitywy oznaczają „brak wartości” i nie są wymienne.
undefined dostajesz wtedy, gdy coś nigdy nie zostało przypisane: zadeklarowana, ale nieustawiona zmienna, brakujący argument funkcji, nieistniejąca właściwość:
null to coś, co piszesz ty, gdy chcesz powiedzieć „celowo puste”:
Przybliżona konwencja: undefined to sposób języka na powiedzenie „nic tu nie ma”, a null to sposób programisty. Oba są falsy, oba nie są równe zwykłym wartościom i oba dostaną później własną stronę.
Symbole: unikalne z założenia
symbol to najrzadziej używany prymityw. Każdy utworzony symbol jest unikalny, nawet jeśli dwa powstały z tego samego opisu:
Symbole przydają się jako klucze obiektów, które nie mogą kolidować z istniejącymi. Biblioteka może dołączyć metadane do twoich obiektów przez symbol i mieć pewność, że żaden inny kod ich nie nadpisze. Spotkasz je ponownie przy iteratorach i dobrze znanych symbolach, takich jak Symbol.iterator.
Sprawdzanie typów w czasie działania
typeof radzi sobie z większością przypadków. Pamiętaj o tych dziwactwach:
Dla null porównuj bezpośrednio: value === null. Dla tablic użyj Array.isArray(value). Na pytanie „czy to jakikolwiek prymityw?” nie ma jednej wbudowanej funkcji, ale idiom jest wystarczająco czytelny:
Prymitywy a obiekty: pułapka przypisania
Jeszcze jedna rzecz warta zobaczenia, zanim pójdziemy dalej. Ponieważ prymitywy są wartościami, a obiekty referencjami, przypisanie działa inaczej:
Przy prymitywach b = a kopiuje wartość. Przy obiektach y = x kopiuje referencję: obie nazwy wskazują na ten sam obiekt. Zmiana przez jedną nazwę wpływa na drugą. To największe pojedyncze źródło błędów typu „zaraz, czemu to się zmieniło?” w JavaScript.
Co warto zapamiętać
- Siedem prymitywów:
string,number,bigint,boolean,null,undefined,symbol. Cała reszta to obiekty. - Prymitywy są niemutowalne i porównywane przez wartość, obiekty są mutowalne i porównywane przez referencję.
typeofpodaje typ w czasie działania, z dwoma dziwactwami wartymi zapamiętania:typeof null === "object"itypeof function === "function".numberto 64-bitowa liczba zmiennoprzecinkowa z sufitem bezpieczeństwa dla liczb całkowitych, abigintistnieje dla dokładnych liczb całkowitych powyżej tego sufitu.
Dalej: stringi i template literals
Stringi to prymityw, którego będziesz używać najczęściej, a template literals (te stringi w backtickach) sprawiają, że ich budowanie jest bezbolesne: interpolacja, tekst wieloliniowy, tagged templates. O tym jest następna strona.
Najczęściej zadawane pytania
Ile typów prymitywnych ma JavaScript?
Siedem: string, number, bigint, boolean, null, undefined i symbol. Cała reszta (tablice, funkcje, daty, zwykłe obiekty) to obiekty. Typ wartości sprawdzisz w czasie działania programu za pomocą typeof, pamiętając o jednym historycznym dziwactwie: typeof null zwraca 'object'.
Czym różni się prymityw od obiektu w JavaScript?
Prymitywy to niemutowalne wartości porównywane przez wartość: dwie 3 to ta sama liczba 3. Obiekty są mutowalne i porównywane przez referencję: dwa {} są różne, nawet jeśli wyglądają identycznie. Przypisanie prymitywu kopiuje wartość, a przypisanie obiektu kopiuje referencję do tych samych danych.
Czy prymitywy w JavaScript naprawdę są niemutowalne?
Tak. Prymitywu nie da się zmienić w miejscu: 'hello'.toUpperCase() zwraca nowy string i nie zmienia oryginału. Ponowne przypisanie zmiennej (x = x + 1) podmienia jej wartość na inny prymityw, ale pierwotna wartość nigdy się nie zmienia. Dlatego const name = 'Ada' nadal pozwala budować nowe stringi na podstawie name.
Dlaczego typeof null zwraca 'object'?
To błąd z pierwszej implementacji z 1995 roku, którego nigdy nie poprawiono, bo zbyt dużo kodu zależało od tego zachowania. Żeby sprawdzić null, porównaj przez ===: value === null. Dla undefined użyj value === undefined albo typeof value === 'undefined'.