Blog

Typy danych w JS... raz jeszcze

PodstawyProgramowanieTypy danych

#JS#javascript#typy#data#types#podstawy

Opublikowany 14.05.2024

Typy danych w JS... raz jeszcze

Od ostatniego wpisu minęło trochę czasu, za co na samym początku chciałbym przeprosić i trochę się wytłumaczyć. Otóż w międzyczasie powiększyła mi się rodzina i obecnie wygospodarowanie wolnej chwili jest dosyć utrudnione 😉

Jak obiecywałem w poście powrotnym - jedną z pierwszych serii, jakie powrócą na bloga będą typy danych w JavaScripcie. Jeśli dobrze pamiętam, to poprzednio skończyliśmy na boolean (choć mogę się mylić, bo to było dobre dwa lata temu). Po krótkim namyśle stwierdziłem, że jednak najlepszym pomysłem będzie nie tyle kontynuowanie serii, co jej ponowne rozpoczęcie. Ma to zdecydowanie więcej sensu oraz pozwoli zachować pewną ciągłość.

Jeśli jednak ktoś już czytał (i pamięta) poprzednią wersję serii, to i tak zapraszam do lektury - wydaje mi się, że doświadczenie zdobyte w tym czasie pozwoli mi napisać ciut więcej i nieco przystępniej (mam nadzieję 😉).

No dobrze, koniec wstępu, przechodzimy do gęstego 😁

Typy danych - cóż to za ustrojstwo?

Zaczniemy może od samych podstaw, czyli czym w ogóle są typy danych. Zgodnie z definicją z Wikipedii typ to opis rodzaju, struktury i zakresu wartości, jakie może przyjmować dany literał, zmienna, stała, argument, wynik funkcji lub wartość. Tyle teorii. Przełóżmy to teraz z polskiego na nasze.

Wszystko, z czym pracujemy w JavaScripcie (i każdym innym języku programowania) to dane. I każda z danych ma swój, jak widzimy w definicji, rodzaj, strukturę i zakres. Innymi słowy, możemy dokonać podziału danych ze względu na to, jakiego są rodzaju (np. literał - czyli ciąg znaków, liczba, wartość logiczna), jaką mają strukturę (np. tablica) oraz jaki mają zakres (np. integer).

Wszystko fajnie, ale po co nam to wszystko?

Dobra, definicję mamy za sobą - ale co to nam daje? Przecież zazwyczaj widzimy, jakie dane mamy do dyspozycji. Prawda? No.... właśnie to nie do końca.

Nawet, jeśli nie pracujemy na danych nam nieznanych (np. pobieranych z API ze słabą dokumentacją), albo nie są to dane wprowadzone przez użytkownika, to typowanie kryje w sobie kilka pułapek. Najbardziej oczywistym przykładem są liczby. Bo czy widząc wartość 123 możemy mieć pewność, że to wartość liczbowa? Przecież string będzie wyglądał identycznie, a wynik operacji na stringach i liczbach będzie diametralnie różny (operacje na pograniczu string/number to chyba jedno z najbardziej memogennych zagadnień w JS).

Wiedząc, jaki typ danych mamy do dyspozycji możemy przygotować odpowiednią strategię pracy z takimi danymi. Różne języki programowania mają różne metody sprawdzania typów danych - ponieważ jednak blog dotyczy frontendu, to posłużymy się JavaScriptem i dane sprawdzimy najprawdopodobniej metodą typeof. I niech wleci przykład!

const variable = 123
const newVariable = '123'


typeof variable 
//number
typeof newVariable 
//string

Proste? Proste. A jak można by to wykorzystać? Załóżmy, że pobieramy dane z API i chcemy, żeby to były dane liczbowe. Możemy zatem sprawdzić, czy to, co dostaniemy na pewno jest liczbą. Na potrzeby przykładu przyjmijmy, że pobierane dane są przechowywane w zmiennej data:

const isNumber = (input) => {
    if (typeof input !== 'number') {
        throw new Error('Type error: input must be a number')
    }
}

try {
    isNumber(data)
} catch (e) {
    console.error(e)
}

Są oczywiście także i inne sposoby na sprawdzenie, czy dana wartość jest typu nubmer, ale o tym napiszę więcej w częściach poświęconych konkretnym typom.

Co mamy na stole?

Dobrze, wiemy już czym są dane oraz po co nam one są. Przejdźmy teraz do tego, co być może zdaniem części z was powinno pojawić się już wcześniej, czyli jakimi typami dysponujemy?

W JS typy danych dzielimy na prymitywne oraz złożone. Do tych pierwszych zaliczamy:

  • String (literał, czyli ciąg znaków),
  • Number (wartość liczbowa),
  • BigInt (duże liczby całkowite),
  • Boolean (wartość logiczna),
  • Undefined,
  • Null,
  • Symbol.

1. String

Ciąg znaków, czyli dowolna wartość, jaka przyjdzie nam do głowy. Daną typu string może być losowy znak z klawiatury lub wklejony Pan Tadeusz. W JS stringi zapisujemy na trzy sposoby: w cudzysłowach, apostrofach lub tzw. backtickach (`).

const quoteString = "Ala ma kota"
const singleQuoteSting = 'Ala ma kota'
const backtickString = `Ala ma kota`

2. Number

Dana liczbowa. Liczby zmiennoprzecinkowe otrzymujemy/deklarujemy zgodnie z amerykańską notacją, czyli poprzez kropkę.

const number = 123
const newNumber = 12.3

3. BigInt

Pozwala operować na naprawdę dużych liczbach całkowitych. I pod określeniem duża liczba kryją się liczby o konkretnych wartościach - mianowicie większych, niż 2^53^. Osobiście nie spotkałem się jeszcze z praktycznym wykorzystaniem typu BigInt, ale warto wiedzieć, że JavaScript daje nam taki typ.

Wartości typu BigInt można rozpoznać po znaku n na końcu liczby:

const largeNumber = 1234567890123456789012345678901234567890n

4. Boolean

Wartość logiczna, przyjmująca wyłącznie wartość prawda (true) lub fałsz (false). Zrozumieniu wartości logicznych warto poświęcić więcej czasu, bo gwarantuję wam, że bez względu na język, jaki wybierzecie booleany będą niezwykle istotne w pisaniu kodu.

const expect = 6
const add = 2 + 4

const isCorrect = expect === add
//result: true

5. Undefined

Trzy ostatnie typy są dość specyficzne, ale zarazem bardzo ważne (szczególnie undefined oraz null). Undefined informuje nas, że dana zmienna nie została zdefiniowana lub nie istnieje. Wartość undefined jest niezwykle istotnym typem, pozwalającym niekiedy uniknąć poważnych błędów w kodzie:

let undefinedVar 

 console.log(undefinedVar) 
 //undefined

Undefined z pewnością dostanie swój osobny wpis, gdzie przy okazji pochylimy się (przynajmniej pobieżnie) nad takimi zagadnieniami, jak hoisting (wynoszenie) oraz closures (domknięcia).

6. null

Null oznacza zmienne bez wartości. I nie jest to to samo, co 0! Przypisanie wartości null do zmiennej dosłownie ją zeruje. W tym momencie może się to wydać zbędne, ale uwierzcie, że w praktyce null okazuje się bardzo przydatnym narzędziem! W tym miejscu jeszcze jedna, ważna uwaga - jeśli sprawdzimy typ zmiennej o wartości null z pomocą metody typeof, to otrzymamy... Object 😉

W zrozumieniu różnicy między wartościami 0, null oraz undefined może pomóc poniższa grafika 😉

js null data

7. Symbol Bardzo ciekawy typ, z którym też jeszcze się nie spotkałem w praktyce. Symbol przechowuje unikalną i niepowtarzalną wartość. I choć nie mamy dostępu do tej wartość (to znaczy - nie możemy jej podejrzeć czy sprawdzić) to mamy pewność, że jest unikalna.

Dobrze, mamy bardzo pokrótce omówione typy proste. Co zatem ze złożonymi? Ponieważ są one niezwykle istotne, to w tym miejscu pozwolę je sobie jedynie wypisać, a we właściwych wpisach poświęcić im odpowiednią uwagę.

Typy złożone:

  • Object (w tym kryją się także tablice, które omówimy sobie oddzielnie)

Ok, a skąd wiadomo, na jakich danych pracujemy?

Wiemy już, co mamy do dyspozycji oraz, przynajmniej w niezbędnym skrócie, co poszczególne typy oznaczają. Pozostaje jeszcze kluczowe pytanie - jak się w tym wszystkim ogarnąć i jak z tym pracować?

W przypadku JavaScriptu interpreter kodu sam rozpoznaje, jakiego typu daną dostaje. Stawia to zatem JS w szeregu języków typowanych dynamicznie. Innymi językami tego typu są np. Python czy PHP.

Wygodne, prawda? Niby tak, ale czasami może przyprawić o niemały ból głowy.

Typowanie dynamiczne - wygoda, czy problemy?

Zacznijmy może od tego, jakie są zalety języków typowanych dynamicznie - bo przecież jakieś muszą być, tym bardziej, że należą do nich 4 języki z top10 najpopularniejszych (na podstawie https://pypl.github.io/PYPL.html).

Przede wszystkim nie musimy się skupiać na tym, jakiego typu daną deklarujemy. Interpreter sam określi typ na podstawie tego, co dostanie. Dodatkowo typ może się zmienić w czasie, w zależności od potrzeb.

To jak? Wygląda fajnie, czy ktoś już zaczął dostrzegać, jakie problemy mogą z tego wyniknąć?

Tak, typowianie dynamiczne niesie ze sobą całą masę problemów. Na samym początku - to, co w pierwszej chwili wydaje się zaletą, w praktyce bardzo często okazuje się wadą. Jak już wspomniałem kilka akapitów wcześniej, bardzo częstym problemem są działania na danych typu string i number. I wystarczy, że interpreter błędnie rozpozna typ zmiennej, żebyśmy otrzymali kompletne głupoty. Albo, w najlepszym wypadku, błąd.

Tak samo możliwość zmiany typu danych w czasie może okazać się zgubna, bo możemy zwyczajnie przeoczyć, że nagle pracujemy na danych zupełnie innego typu, niż powinniśmy.

I właśnie dlatego pozostałe 6 języków z top10 to języki typowane statycznie...

Statecznie statyczny

Jak zapewne się domyśliliście, języki typowane statycznie wymagają określenia typu danej przy jej deklaracji. Dzięki temu można już na wstępnym etapie zabezpieczyć się przed wieloma błędami związanymi z typowaniem. Fakt, wymaga to poświęcenia nieco więcej czasu na początku, ale na późniejszym etapie odwdzięczy się znacznie lepszą kontrolą nad naszymi danymi oraz pozwoli na znacznie lepszą i wygodniejszą kontrolę błędów.

Zaliczają się do nich między innymi C#, GO czy Java.

Czy front się załamał, a my jesteśmy zgubieni?

Nie.

Dziękuję, można się rozejść 😋

Teraz nieco bardziej poważnie. Otóż JavaScript jest o tyle niezwykłym językiem, że de facto w top10 języków programowania występuje dwa razy 😉 po raz pierwszy na miejscu trzecim w swojej podstawowej, dynamicznej wersji. Po raz drugi na miejscu ósmym jako TypeScript.

Czym jest ten wynalazek? Nazwa sugeruje, że mamy do czynienia z JavaScriptem typowanym statycznie, jednak nie jest to do końca prawda.

TypeScript może być typowany statycznie, ale tak naprawdę pod maską kryje naszego starego, dobrego JSa. Czyli wszystko, co można zrobić w JavaSripcie zadziała też w TSie. A jeśli w TSie nie zadeklarujesz typu danej? Cóż... to nic się nie stanie, choć przyznam, że byłoby to nieco bez sensu 😂

const dynamicTypeVariable = 111
const staticTypeVariable: number = 111

A co nam to daje? Załóżmy, że gdzieś przez pomyłkę zamiast liczby 111 podaliśmy string '111'. W takim przypadku interpreter JavaScript zwyczajnie przypisze naszej zmiennej typ string i zadowolony pójdzie pykać w CSa. Tymczasem TypeScript narobi wrzasku, że mu się typy nie zgadzają. Czyli de facto uchroni nas przed głupim - a potencjalnie poważnym w skutkach - błędem niemal od razu.

Czyli co, JS do kosza?

Nie!!! Po pierwsze - cały czas jest baaaardzo dużo projektów napisanych w JSie. Po drugie - i to nie tylko moja opinia - zdecydowanie lepiej zacząć naukę od JavaSriptu!!! W ten sposób będziecie mieć możliwość poznać dokładnie ten język i przy późniejszej migracji na TypeScript wiedzieć, co jest elementem javascriptowego fundamentu, a co nadbudową daną nam przez TS. I po trzecie - niekiedy, zwłaszcza w jakiś mniejszych projektach, JS jest w zupełności wystarczający 😊

Dobrze, jak na początek wyszło tego całkiem sporo. Ponieważ jestem leniem, to cały czas nie zaimplementowałem systemu komentarzy, dlatego z pytaniami czy uwagami wpadajcie na mojego Twixera 😉

Do następnego!