19. ledna 2025

Metody primitivů

JavaScript nám umožňuje pracovat s primitivy (řetězci, čísly atd.), jako by to byly objekty. Poskytuje i metody, které na nich můžeme volat jako na objektech. V dalších kapitolách je prostudujeme, ale nejprve se podíváme, jak to funguje, protože primitivy samozřejmě nejsou objekty (a zde to ještě více ozřejmíme).

Podíváme se na klíčové rozdíly mezi primitivy a objekty.

Primitiv:

  • Představuje hodnotu primitivního typu.
  • Existuje 7 primitivních typů: string, number, bigint, boolean, symbol, null a undefined.

Objekt:

  • Dokáže obsahovat více hodnot uložených ve svých vlastnostech.
  • Může být vytvořen pomocí {}, např. {jméno: "Jan", věk: 30}. V JavaScriptu jsou i jiné druhy objektů, například funkce jsou objekty.

Jedna z nejlepších věcí na objektech je, že jako jejich vlastnost můžeme uložit funkci.

let jan = {
  jméno: "Jan",
  řekniAhoj: function() {
    alert("Ahoj kámo!");
  }
};

jan.řekniAhoj(); // Ahoj kámo!

Zde jsme tedy vytvořili objekt jan s metodou řekniAhoj.

JavaScript obsahuje mnoho vestavěných objektů, například ty, které pracují s daty, chybami, HTML prvky a podobně. Mají různé vlastnosti a metody.

Tyto vlastnosti však mají svou cenu!

Objekty jsou „těžší“ než primitivy. Vyžadují více zdrojů, které zatěžují vnitřní stroj.

Primitiv jako objekt

Tvůrci JavaScriptu čelili následujícímu paradoxu:

  • Existuje mnoho věcí, které člověk chce dělat s primitivy, jakými jsou řetězec nebo číslo. Bylo by skvělé přistupovat k nim pomocí metod.
  • Primitivy musejí být co nejrychlejší a co nejmenší.

Řešení vypadá trochu těžkopádně, ale je zde:

  1. Primitiv je pořád primitiv. Jednoduchá hodnota, po jaké toužíme.
  2. Jazyk umožňuje přístup k metodám a vlastnostem řetězců, čísel, booleanů a symbolů.
  3. Aby to fungovalo, vytvoří se speciální objekt zvaný „obal“ neboli „wrapper“, který tuto přídavnou funkcionalitu poskytne a pak bude zničen.

Tyto „objektové obaly“ jsou pro každý primitivní typ jiné a nazývají se: String, Number, Boolean, Symbol a BigInt. Poskytují tedy různé sady metod.

Například existuje řetězcová metoda řetězec.toUpperCase(), která vrací řetězec zapsaný velkými písmeny.

Funguje následovně:

let řetězec = "Ahoj";

alert( řetězec.toUpperCase() ); // AHOJ

Jednoduché, že? Ve skutečnosti se v řetězec.toUpperCase() děje následující:

  1. Řetězec řetězec je primitiv. V okamžiku přístupu k jeho vlastnosti se tedy vytvoří speciální objekt, který zná hodnotu tohoto řetězce a obsahuje užitečné metody, např. toUpperCase().
  2. Tato metoda se spustí a vrátí nový řetězec (který je zobrazen funkcí alert).
  3. Speciální objekt se zničí a zůstane samotný primitiv řetězec.

Primitivy tedy mohou poskytovat metody, ale samy zůstávají malé.

JavaScriptový motor tento proces značně optimalizuje. Může dokonce úplně vynechat vytvoření nového objektu. Stále však musí dodržovat specifikaci a chovat se tak, jako by jej vytvořil.

I čísla mají své vlastní metody, například toFixed(n) zaokrouhluje číslo se zadanou přesností:

let n = 1.23456;

alert( n.toFixed(2) ); // 1.23

Další specifické metody uvidíme v kapitolách Čísla a Řetězce.

Konstruktory String/Number/Boolean jsou jen pro interní použití

Některé jazyky, např. Java, nám umožňují explicitně vytvářet „obaly“ pro primitivy pomocí syntaxe typu new Number(1) nebo new Boolean(false).

V JavaScriptu je to z historických důvodů také možné, ale důrazně nedoporučované. Skript se začne chovat bláznivě hned na několika místech.

Například:

alert( typeof 0 ); // "number"

alert( typeof new Number(0) ); // "object"!

Objekty jsou v if vždy pravdivé, takže zde se zobrazí hlášení:

let nula = new Number(0);

if (nula) { // nula je pravdivá, protože je to objekt
  alert( "nula je pravdivá!?!" );
}

Naproti tomu použití stejné funkce String/Number/Boolean bez new je zcela správná a užitečná věc. Funkce převede hodnotu na odpovídající typ: na řetězec, na číslo nebo na boolean (na primitiv).

Například tohle je zcela v pořádku:

let číslo = Number("123"); // převede řetězec na číslo
null/undefined nemají žádné metody

Speciální primitivy null a undefined představují výjimky. Nemají odpovídající „obaly“ a neposkytují žádné metody. V určitém smyslu slova jsou vlastně „ty nejprimitivnější“.

Pokus o přístup k vlastnosti takové hodnoty ohlásí chybu:

alert(null.test); // chyba

Shrnutí

  • Primitivy s výjimkou null a undefined poskytují mnoho užitečných metod. Prostudujeme je v následujících kapitolách.
  • Formálně tyto metody pracují na dočasných objektech, ale JavaScriptové motory jsou dobře vyladěny, aby je vnitřně optimalizovaly, takže jejich volání není nákladné.

Úlohy

důležitost: 5

Uvažujme následující kód:

let řetězec = "Ahoj";

řetězec.test = 5;

alert(řetězec.test);

Co myslíte, bude to fungovat? Co se zobrazí?

Zkuste si to spustit:

let řetězec = "Ahoj";

řetězec.test = 5; // (*)

alert(řetězec.test);

Podle toho, zda máte use strict nebo ne, výsledek může být:

  1. undefined (nestriktní režim).
  2. Chyba (striktní režim).

Proč? Přehrajme si, co se děje na řádku (*):

  1. Když přistoupíme k vlastnosti proměnné řetězec, vytvoří se „obal“.
  2. Ve striktním režimu zápis do něj znamená chybu.
  3. V nestriktním režimu bude operace s touto vlastností provedena, objekt získá vlastnost test, ale poté „obal“ zmizí, takže na posledním řádku nemá řetězec po této vlastnosti ani stopu.

Tento příklad jednoznačně dokazuje, že primitivy nejsou objekty.

Nelze do nich ukládat další data.

Mapa tutoriálu

Komentáře

přečtěte si před komentováním…
  • Máte-li návrhy na zlepšení, vytvořte prosím issue na GitHubu nebo pull request místo komentáře.
  • Pokud v článku něčemu nerozumíte, napište prosím, čemu přesně a na kterém místě.
  • Pro vložení několika slov kódu použijte značku <code>, pro několik řádků je obalte značkou <pre>, pro více než 10 řádků vložte odkaz na pískoviště (plnkr, jsbin, codepen…)