Operátor přiřazení a srovnání jsme se naučili do velmé míry používat už na základce.
Problémem je, že jsme pro obě operace používali jediný symbol =
.
S počítačem musíme komunikovat vždy jednoznačně. Tedy musíme se naučit rozlišovat dvě různé operace přiřazení a srovnání.
Přiřazení hodnoty
Přiřazovací operátor je nejčastěji používaným operátorem. Příkaz nastavuje hodnotu proměnné nalevo od = na hodnotu výrazu, který je vpravo. Ukázka:
var x;
x = 42;
Můžeme přiřazovat také více hodnot naráz. U přiřazení platí pravidlo right to left
, neboli z prava do leva. Znamená to, že hodnota pravé strany je přiřazovana levé straně. Příklad:
var x, y, z;
x = y = z = 42
Porovnání hodnot
Operátor porovnávání v programech funguje jako otázka: "Je tato hodnota X stejná jako tato hodnota Y?". Co přesně však v JS znamená "stejná jako"?
V javascriptu máme hned dva porovnávací operátory a každý má jiný pohled na to co je stejné či shodné. Hlavní rozdíl mezi nimi je, jak ověřují datové typy proměnných^compare a jak se přetypovávají.
Striktní operátor porovnání neprovádí přetypování (coercion), oproti tomu dvojité rovná se provede v případě potřeby přetypování automaticky.
Dvojité (loosy) rovná se
Podle specifikace (7.2.14 Abstract Equality Comparison^ecma), se neprve zkontroluje zda oba operandy mají stejný datový typ. Pokud mají, pak dojde ke stejnému srovnání hodnot jako v případě striktního porovnání. Programátoři často opomíjejí, že přetypování se dotýká i srovnávacího operátoru.
Přetypování se netýká typů reprezentující prázdnou hodnotu: undefined
a null
. Obě jsou v případě tohoto operátoru považovány za shodné.
Když je alespoň jeden z operandů objekt, zavolá se jeho abstraktní operace a pokusí se jej přetypovat na jiný datový typ. Primitivní datové typy se přetypují na stejný typ a následně se porovnájí hodnoty stejným způsobem jako dělá striktní rovná se.
Striktní (trojité) rovná se
Pokud budeme porovnávat dva operandy různého datového typu dostaneme vždy jako výsledek false
.
Má smysl tedy striktně porovnat pouze hodnoty stejného datového typu.
U objektů se porovnává jejich identita. Dva objekty se nebudou rovnát i když budou mít stejnou strukturu a hodnoty. Jejich reference musí odkazovat na stejné místo v paměti.
U proměnných stejných primitivních datových typů dojde k porovnání hodnot.
Tady však pozor, existují dvě vyjímky v případě typu number
.
Pokud máme
- obě hodnoty
NaN
. Ačkoliv hodnoty jsou stejné, dostanemefalse
. - nebo porovnáváme dvě hodnoty
-0
a+0
. Javascript má kladnou a zápornou nulu, ale snaží se vytvářet dojem, že nula je jedna.
Pokud potřebujeme srovnat i tyhle dvě hodnoty, je lepší se ===
vyhnout. Metodu Object.is(..)
lze považovat za "čtyřnásobné rovnítko" ====, tedy za opravdu přísné porovnávání!
Nebo speciálně pro porovnání NaN
použijte metodu Number.isNaN(..)
a pro porovnání -0 použijte utilitu Object.is(..)
, která také nelže.
Operátor nerovná se
Operátory porovnání vrací logickou hodnotu, je praktické mít v jazyku i logickou negaci těchto operátorů. Dvojité rovná se má svůj protějšek v operátoru !=
a striktní rovná se v !==
.
Jaké porovnání vybrat
Ačkoliv JS je dynamický typovaný, neznamená to, že bysme měli ignorovat typy s tím, že si vystačíte s dvojitým rovná se a javascript se o zbytek postará a zkonvertuje je. Kód se striktním porovnáním je více předvídatelný a může dojít k méně okrajovým případům, kterých je u dvojitého rovná se spousta a jsou častým zdrojem chyb a nepochopení^3 🤯. Striktní srovnání by mělo být i rychlejší.
Při programování bývá praktičtější používat jednu hodnotu reprezentující prázdnou hodnotu než dvě, kterými jsou null
a undefined
.
V tomto případě je přehlednější použít dvojité rovná se. Všechny 3 řádky jsou zaměnitelné.
if (x === null || x === undefined) {}
if (x == null) {}
if (x == undefined) {}
Při výběru operátoru bysme si měli položit dvě otázky. Víme přesně jaké datové typy budeme srovnávat? A pomůže si na tomto místě s přetypováním (nestane se kód příliš polymorfní)? Není pro nás používání dvojitého == příliš nepředvídatelné?