Operátory přiřazení a srovnání hodnot

AndreaAndrea

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é, dostaneme false.
  • 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é?