Konverze typů

AndreaAndrea

Prakticky každý programovací jazyk, se potýká s typovými konverzemi. V Javascriptu se s typovou konverzí vypořádáváme s pomocí abstraktních operací.

Máme možnost v objektech definovat metody, jako jsou toPrimitive, toString, toNumber, toBoolean, díky kterým implementujeme vlastní způsob konverze. Existují i další abstraktní operace které se používají, ale popíšeme si jen ty na které pravděpodobně běžně narazíte. Pokud vás zajímají i jiné, přečtěte si specifikaci.

K automatickému volání abstraktních operací, dochází nejčastěji v situacích

  • kdy kontrolujeme splnění podmínky v if nebo v ternárním operátoru, kde očekáváme boolean s odpovědí pravda/nepravda.

  • když používáme operátor dvojité rovná se.

  • pokud se objekt použije jako operand v operaci konkatenace +.

  • pokud jeden z operandů není číselný a pracujeme s aritmetickými operacemi nebo s operacemi srovnání.

  • objekt se použije jako argument pro funkci Number() nebo String().

  • v template literals používáme proměnné, které se převádí na text

ToPromitive

První abstraktní operace, kterou si představíme se nazývá ToPrimitive. Spousta operátorů JS funguje pouze s primitivními datovými typy a v tomto případu pokud se v operaci ocitne objektový typ (např. objekt, pole, funkce), musíme ho neprve přeměnit na primitivní datový typ. V tomto je JS specifičtější, protože v mnoha jiných technologiích pokud se o tohle pokusíme dostaneme spíše chybu. Tato abstraktní operace se na téhle konverzi bude podílet.

Pro objekty se metoda ToPrimitive() chová tak, že nejprve hledá existenci metody valueOf() a pokud tuto metodu nenajde, použije metodu toString(). Pokud ani tato metoda není k dispozici, vyhodí se chyba TypeError.

Pokud návratový výsledek z ToPrimitive není primitivní typ, ale jde o jiný objekt než jsme dříve vložili, pak se s tímto dalším objektem vyvolá toPrimitive znovu a bude se vyvolávat tak dlouho, dokud nedostaneme něco, co je skutečným primitivem, nebo nedostaneme chybu. Výsledek ToPrimitive tedy a skončí buď voláním valueOf(), toString() nebo chybou.

Nápověda typu

Jeden z volitelných argumentů metody je "hint", jehož smyslem je naznačit, pokud máte něco, co není primitivní, jaký typ byste preferovali dostat. Implicitní hodnota nápovědy je "default". Pokud však provádíte číselnou operaci, která vyvolá ToPrimitive, jako nápovědu pošle "number". V jiném případě při práci s řetězci, dostanete nápovědu "string".

Pokud v nápovědě příjde např. hodnota "number", pak se nejprve pokusím vyvolat hodnotu valueOf() zda je primitivní. Pokud vrátí primitivum, pak jsme hotoví. Pokud mi to primitivum nedá nebo neexistuje, pak zkusí zavolat toString().

Další argument je "preferredType" (pouze v ECMAScript 6), který určuje, zda se má použít metoda valueOf nebo toString.

ToString

Téměř každá hodnota, která v JS existuje má alespoň nějaký druh reprezentace ve formě řetězce.

Konverze primitivních typů na string

Metoda toString^tc39-tostring je dostupná pro všechny primitivní typy (např. number, boolean), u kterých vrátí řetězec, který představuje jejich hodnotu. Čísla je možné velice jednoduše převést na řetězce, ale u opačné konverze je situace složitější. Z tohoto důvodu jsou v jazyce k dispozici funkce parseInt()parseFloat(). U nulové nebo nedefinovaná hodnoty, vrátí se řetězec "null" nebo "undefined", jak byste nejspíše čekali.

Konverze ostatních typů na string

Pokud je toString volána nad objektem, vrátí se řetězec, který představuje tento objekt.

Pole mají vlastní toString logiku, která serializuje položky pole. Nicméně může se zdát zvláštní, že se vynechávají závorky. Takže pokud serializujete prázdné pole, dostanete prázdný řetězec.

[].toString(); // ""
[1,2,3].toString(); // "1,2,3"
[null, undefined].toString(); ; // "," 
[[[],[],[]], []].toString(); // ",,," 
[,,,,].toString();// ",,," 

V případě objektů jako je Date, vrací metoda reprezentaci data a času.

V vlastních objektů, které nemají svou metodu toString, tak se ve výchozím stavu volá Object.prototype.toString, který nám vrátí reprezentaci typu objektu (string tag). Můžete však prototype přepsat a naimplementovat logiku, ve které se objekt stringifikuje do JSONu apod.

Uvědomte si, že metoda toString() je automaticky volána například pokud se snažíte spojit řetězec s jinou hodnotou pomocí operátoru "+", nebo při výpisu na konzoli apod.

ToNumber

Metoda toNumber() slouží k převodu hodnoty na číslo. JS se pokouší o konverzi, aby získal číselnou hodnotu, která je použitelná pro výpočet.

Konverze primitivních typů na number

Je dostupná pro všechny primitivní typy (např. string, boolean). Tato metoda se obvykle volá, když se pokoušíte použít algebraitské oprátory na objektu s číslem.

U konverze řetězce na číslo nás může překvapit

"" // 0
"0" // 0
"0xaf" // 175

U null dostáváme číselnou hodnotu 0 a u "undefined" zase NaN.

V případě, že je hodnota, na které je metoda volána, jiný objekt nebo primitivní typ, který nelze konvertovat na číslo, metoda vrací NaN.

Konverze ostatních typů na number

Pokud je metoda volána s objektem, vrátí se hodnota, která reprezentuje tento objekt jako číslo.

Například volání new Date().toNumber() vrátí timestamp reprezentující datum a čas.

V případě, že je argumentem funkce Number objekt, pak se volá metoda toPrimitive s nápovědou number. U objektů a polí, pokud nejsou implementovány vlastní abstraktní operátory se volá valueOf. Ve většině případů bude metoda valueOf vracet jen sama sebe. Což bude mít za následek, že se valueOf ignoruje a zavolá se toString. To vede k tomu, že v různých operacích, kde jste očekávali primitivum, ale chtěli jste primitivní číslo, dostanete primitivní řetězec.

A pak nastoupí další koerce. Takže nakonec skončíme s toString a na to, co toString vrátí, což může být standardní řetězec objektu z Object.prototype.toString. Což rozhodně není reprezentace čísla, takže dostaneme nakonec NaN. To je vlastně rozumné. Je to hloupé, ale je to rozumné. Pokud přepíšete valueOf pro nějaký objekt, můžete vrátit, co chcete.

ToBoolean

Logika, kterou JS používá k převodu hodnot na hodnoty typu boolean je poměrně jednoduchá. Specifikace definuje pár hodnot, které jsou vyhodnoceny jako false. Tedy všechno co je undefined, null, false, 0, NaN, "" (prázdný řetězec) je konvertováno na false. Jinak ostatní hodnoty jsou konvertovány na true.

ValueOf

Existují dvě metody, které mohou být k dispozici na jakémkoli ne-primitivu, těmi jsou valueOf()toString(). Pokud je metoda volána na objektu, který má implementovanou i metodu toString(), JavaScript nejdříve volá metodu valueOf() a pokud vrátí hodnotu jinou než objekt, použije ji. Pokud vrátí objekt, volá na něm metodu toString().