Ta strona używa cookies
sprawdź politykę prywatności

Rozumiem

Konwersja zmiennych w JavaScript czyli ‘==’ vs ‘===’.

11/6/2019
Bartek Cis

Zastanawiasz się czasem jaka jest w zasadzie różnica między ‘==’ a ‘===’ kiedy porównujesz zmienne w JavaScript? I jaką rolę w tym wszystkim ma konwersja zmiennych (ang. coercion)?

Czego się dzisiaj dowiesz?

Mam zamiar opisać różnicę między metodami na porównywanie zmiennych w JavaScript tzn. == vs ===. Sam proces nie jest aż tak skomplikowany ale w tle dzieją się mechanizmy o których warto wiedzieć. Jakie?

No właśnie będzie trochę o zmiennych w JavaScript, jak w ogóle funkcjonują i jak działa konwersja zmiennych – coercion. Jak nie chce Ci się czytać całego teoretycznego wstępu po prostu kliknij tutaj.

Dlaczego o tym piszę?

Pracuje m.in. z deweloperami których natywnym językiem nie jest JavaScript ale tym JS’em się interesują. Zauważam brak zrozumienia dynamicznego systemu typów który obowiązuje w JS’ie. Nie wiem czy i ile innych języków programowania działa w podobny sposób ale posiadając jakąś wiedzę zakładają, że w JSie jest tak samo.

Nie są to rasowi JS deweloperzy więc pół biedy ale co jeśli Ty się za takowego uważasz a konwersja zmiennych dalej jest dla Ciebie niezrozumiała? No właśnie…

Ahhh… I na początku wyjaśnię – jestem fanem TypeScript’a.

Zmienne w JavaScript

Na początku należy sobie uzmysłowić jedną ważną rzecz:

Typy w JS nie są dowiązane/przypisane do nazwy zmiennej!

Czym są typy?

Gdybyś jeszcze nie wiedział/a czym są typy to tak szybko, mamy typy prymitywne ( primitive ) : null, undefined, boolean, number, string, symbol. Planowane jest też dodanie nowego typu prostego – BigInt. Typem złożonym jest object który z kolei ma podtypy takie jak np. array i function.

Value as Type

No właśnie. Jeśli tworzysz zmienną w czystym JS’ie to typ nie jest przyporządkowany do samej zmiennej ale do wartości tej zmiennej!

Wartość zmiennej może się zmieniać nie tylko w kategorii ilościowej (np. z 3 na 15) ale też jej typ może ulec modyfikacji w czasie cyklu życia tej zmiennej. Znaczy to tyle, że wartość danej zmiennej może mieć na początku typ string and potem być np. undefined.


Zjawisko zmiany typu wartości zmiennej z angielskiego nazywa się coercion. Ja dla ułatwienia używam tłumaczenia konwersja.

Coercion

Cała sztuczka polega na tym, że JavaScript jest skonstruowany w ten sposób, że taka konwersja może pojawić się na wielu różnych etapach ( o czym się przekonasz kiedy będę rozkładał na czynniki pierwsze operator ‘==’ ). Deweloper niekoniecznie musi być tego świadomy i wtedy konstruuje swój kod w sposób który potencjalnie wprowadza do programu różne bugi.

Implicit Coercion

To jeden z rodzajów konwersji. Ten bardziej problematyczny o którym wspomniałem powyżej czyli dzieje się to bez bezpośredniej ingerencji dewelopera tzn. JavaScript sam wykonuje taką podmiankę. Tak już po prostu jest.


Jak widać powyżej JS sam dopasował typ wartości przed wykonaniem operacji dzielenia. Zamienił string na number.

Przez ten mechanizm deweloper który nie zna dobrze całego sposobu myślenia JS’a porusza się czasem po omacku. Z drugiej strony jak ktoś to wszystko dobrze ogarnia to może sobie ułatwić życie i redukować zbędne linijki kodu.

Explicit Coercion

Drugi rodzaj jest wtedy kiedy deweloper świadomie zmienia typ.


Teraz jest wszystko jasne. Zanim doszło do operacji dodawania string został zamieniony na number przez dewelopera. Efekt przemawia sam za siebie 🙂

Gdzie się dowiedzieć więcej?

Zachęcam do lektury YDNJS, dobre miejsce na początek a potem… Potem dokumentacja 😛

TypeScript

Abstrahując od poziomu zrozumienia konwersji przez deweloperów ten mechanizm jest furtką do problemów w pisanych programach. Dlatego powstał nowy ( bo chyba można go tak nazwać ) język programowania – TypeScript.

Zasadniczo bazuje on na JavaScript jednak posiada statyczny system typów gdzie typy są przypisane do zmiennej nie do wartości 🙂 Problem konwersji typów wtedy przestaje być kłopotliwy i w razie czego widzisz odpowiedni błąd w konsoli.

Dobra dopływam do brzegu…

== vs ===

Operatory porównywania wykorzystuje się często i gęsto np. przy instrukcji warunkowej if i tego będę używał w przykładach. Na początek=== bo prostsze 🙂

Strict comparison  ‘===’

Albo inaczej non-coercive comparison nazwa wskazuje na to, że żadna forma konwersji w tym wypadku nie jest możliwa. JS sprawdza, czy dwie zmienne posiadają wartości o tym samym typie. Jeżeli tak jest to logika wewnątrz może zostać wykonana.

Zakładam, że nie było tu dla Ciebie większej niespodzianki 🙂 Osobiście tą metodę uważam za dobrą praktykę i staram się ją stosować w zasadzie wszędzie. Minimalizuje Implicit Coercion do niezbędnego minimum.

Jeśli interesuje Cię dokładnie jak wygląda ten algorytm w specyfikacji to będzie tak:

The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is different from Type(y), return false.
  2. If Type(x) is Undefined, return true.
  3. If Type(x) is Null, return true.
  4. If Type(x) is Number, then
    • If x is NaN, return false.
    • If y is NaN, return false.
    • If x is the same Number value as y, return true.
    • If x is +0 and y is −0, return true.
    • If x is −0 and y is +0, return true.
    • Return false.
  5. If Type(x) is String, then return true if x and y are exactly the same sequence of characters (same length and same characters in corresponding positions); otherwise, return false.
  6. If Type(x) is Boolean, return true if x and y are both true or both false; otherwise, return false.
  7. Return true if x and y refer to the same object. Otherwise, return false.

Loose comparison ‘==’

Lub coercive comparison – bez zaskoczenia – JS pozwala na konwersję typów przy porównaniu. Wykonuje wtedy specjalny algorytm o którym za moment jednak jeszcze chwila dla tego kwiatka:

Value assignment ‘=’

Widziałem próby przypisywania wartości zmiennej wewnątrz instrukcji if:


Po pierwsze instrukcja warunkowa if nie do tego służy aby wewnątrz warunku cokolwiek przypisywać poza tym to nie ma najmniejszego sensu bo kod wewnątrz się zawsze wykona… Proszę Cię nie próbuj tak robić 🙂

Jeśli spróbujesz porównać dwie zmienne za pomocą = to program powinien się wysypać (i chwała JS’owi za to 🙂 ).

Algorytm ‘==’

A więc klasyczny loose comparison wygląda tak:


Number jest porównany do Stringa i w rezultacie otrzymujemy true. Ewidentna konwersja… Co się dzieje pod powierzchnią? Najpierw wytłumaczenie a potem schemat z dokumentacji 🙂

  1. Jeżeli typy są takie same to wykonaj algorytm ‘===’.
  2. Jeżeli jedna wartość jest null a druga undefined zwróć true czyli wykonaj logikę.
  3. Jeżeli jedna wartość jest number a druga string to przekonwertuj string do number. Number jest preferowanym typem.
  4. Jeżeli jedna wartość jest prymitywna np. string lub number a druga jest obiektem, przekonwertuj obiekt do wartości prymitywnej.

To by było tak w skrócie. Dosłownie dokumentacja opisuje to tak:

  1. If x is null and y is undefined, return true.
  2. If x is undefined and y is null, return true.
  3. If Type(x) is Number and Type(y) is String,
    return the result of the comparison x == ToNumber(y).
  4. If Type(x) is String and Type(y) is Number,
    return the result of the comparison ToNumber(x) == y.
  5. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
  6. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
  7. If Type(x) is either String or Number and Type(y) is Object,
    return the result of the comparison x == ToPrimitive(y).
  8. If Type(x) is Object and Type(y) is either String or Number,
    return the result of the comparison ToPrimitive(x) == y.
  9. Return false.

Ciekawą sprawą jest to, jak JS konwertuje typy złożone do prymitywnych… Ale o tym może kiedy indziej 🙂
Znalazłem bardzo fajne interaktywne narzędzie w którym można się tym pobawić.

Podsumowanie

Jeśli dotrwałeś/aś do tego miejsca możesz się zastanawiać: “WTF? Co ja tu czytam? Ale pierdoły… ” No tak… cytowanie dokumentacji to trochę przesada 🙂 Ale pisać o coercion po prostu trzeba. Nawet w erze TypeScript’a i jemu podobnych.

Cała koncepcja tego artykułu jest taka, że konwersja zmiennych jest w JS na porządku dziennym. Jest duża różnica jeśli użyjesz == lub === więc używaj ich świadomie.

Podziel się z innymi 🙂

Cześć jestem Bartek.

Na tym blogu wprowadzę Cię w tajniki Front-Endu i programowania webowego.

Warto
Social media & sharing icons powered by UltimatelySocial