NOVINKA: Staň se datovým analytikem od 0 Kč a získej jistotu práce, lepší plat a nové kariérní možnosti. Více informací:

Lekce 3 - Vlastnosti objektů a konstruktory v JavaScriptu

V minulé lekci, První objektová aplikace v JavaScriptu, jsme si napsali svou první objektovou aplikaci.

V tomto tutoriálu objektově orientovaného programování v JavaScriptu se budeme věnovat vlastnostem. Již umíme definovat třídy a jejich metody reprezentující akce, které mohou vykonávat. Novou instanci umíme následně z třídy vytvořit pomocí klíčového slova new. Také víme, že na třídách můžeme definovat vlastnosti, což jsme ještě nezkoušeli. Právě vlastnosti si tedy dnes objasníme.

Vlastnosti v JavaScriptu

Vytvořme si nový ukázkový program, tentokrát budeme reprezentovat firmy. Zamysleme se, co by naše firmy měly obsahovat za vlastnosti. Každé firmě přiřadíme název, sídlo a seznam zaměstnanců.

Ve složce js/ vytvoříme soubor Firma.js. Zatím si do něj přidáme pouze prázdnou třídu Firma:

class Firma {

}

S třídami budeme opět pracovat v nějakém dalším souboru. Ve složce js/ si tedy vytvoříme soubor, který se bude jmenovat obsluha.js. V něm si vytvoříme novou instanci naší firmy a uložíme si ji do proměnné microsoft:

const microsoft = new Firma();

Nezapomeňme si soubory Firma.js a obsluha.js načíst v tomto pořadí v souboru index.html:

<body>
    <script src="js/Firma.js"></script>
    <script src="js/obsluha.js"></script>
</body>

Pokud bychom načetli skript pro obsluhu před skriptem, který definuje třídu, dojde k chybě. Skript pro obsluhu by se totiž pokoušel odkázat na třídu, která ještě nebyla definována.

Další způsoby importování souborů si ukážeme dále v kurzu.

Definice vlastností

Vlastnosti třídy jsou de facto proměnné, které na sobě budou mít instance dané třídy. Můžeme k nim přistupovat z metod uvnitř třídy, aniž bychom je museli předat jako parametr. A lze k nim přistupovat i zvenčí, na konkrétní vytvořené instanci.

Definujme naší instanci microsoft vlastnost nazev. K vlastnostem instancí přistupujeme přes tečku .. To značí, že co budeme psát za tečku, patří naší instanci. Dále uvedeme název naší vlastnosti následovaný přiřazením hodnoty pomocí rovnítka =. Zápis vypadá takto:

microsoft.nazev = "Microsoft Corporation";

Přidejme si tento řádek do obsluha.js a zkusme si obsah naší vlastnosti vypsat na stránku. Kompletní soubor obsluha.js vypadá takto:

const microsoft = new Firma();
microsoft.nazev = "Microsoft Corporation";
document.write(microsoft.nazev);

Otevřeme si nyní naši stránku v prohlížeči:

Tvoje stránka
localhost

Jak vidíme, vlastnost naší instance se vypsala. Stejným způsobem můžeme hodnotu již existující vlastnosti i měnit:

const microsoft = new Firma();
microsoft.nazev = "Microsoft Corporation";
document.write(microsoft.nazev);

microsoft.nazev = "Microsoft";

document.write("<br />");
document.write(microsoft.nazev);

Výsledek:

Tvoje stránka
localhost

Takto bychom mohli pokračovat a vytvořit více instancí firem s různými názvy:

const microsoft = new Firma();
microsoft.nazev = "Microsoft Corporation";
const google = new Firma();
google.nazev = "Google LLC";
document.write(microsoft.nazev);
document.write("<br />");
document.write(google.nazev);

Výsledek:

Tvoje stránka
localhost

Třídní atributy

Protože je naše třída prázdná, museli bychom vlastnost nazev takto přidat i pro další budoucí instance třídy Firma, aby ji měly všechny. Při větším počtu vlastností nebo instancí by to vedlo k opakování kódu a snadno bychom na některou vlastnost zapomněli. Takový postup není praktický.

Proto novější verze JavaScriptu umožňují definovat tzv. třídní atributy (anglicky class fields). Jde o vlastnosti, které definujeme přímo v těle třídy a které se vždy automaticky přiřadí každé instanci dané třídy.

Při použití třídních atributů tak máme vždy zaručeno, že objekt bude obsahovat požadované vlastnosti. Zároveň je náš kód čitelnější, protože pouhým pohledem na třídu dokážeme zjistit, jaké vlastnosti její instance obsahují.

Definujme naší třídě Firma pro vlastnost nazev třídní atribut nazev:

class Firma {
    nazev;
}

Vidíme, že definice třídního atributu je přímočará – přímo do těla napíšeme jeho název a nepovinně jej zakončíme středníkem.

Pokud si zobrazíme naši stránku, tak zůstane její obsah beze změny:

Tvoje stránka
localhost

Výchozí hodnoty

Odeberme ze souboru obsluha.js řádky přiřazující jednotlivým firmám vlastnosti:

const microsoft = new Firma();
const google = new Firma();
document.write(microsoft.nazev);
document.write("<br />");
document.write(google.nazev);

Vlastnost nazev našich instancí bude nyní obsahovat speciální hodnotu undefined. Přesvědčme se na obsahu stránky:

Tvoje stránka
localhost

Hodnota undefined zde značí, že daná vlastnost se na objektu buď vůbec nenachází, anebo nachází, ale ještě nemá přiřazenou hodnotu. U nás se jedná o ten druhý případ. Naše instance díky třídnímu atributu nazev obsahují vlastnost nazev, vlastnost však ještě nemá přiřazenou žádnou hodnotu.

K hodnotě undefined se v budoucích lekcích ještě vrátíme.

Většinou je však žádoucí, aby instance tříd měly po jejich vytvoření inicializované všechny své vlastnosti na nějaké smysluplné hodnoty. Přiřaďme proto atributu nazev ve třídě Firma výchozí hodnotu "Název firmy":

class Firma {
    nazev = "Název firmy";
}

Jako výchozí hodnota atributu se použije hodnota výrazu, který následuje po rovnítku = za názvem atributu. Takovým výrazem může být například číslo, řetězec, pole, volání funkce a další.

Všechny firmy budou mít nyní název Název firmy:

Tvoje stránka
localhost

Třídní atributy firmy

Doplňme nakonec ještě naši třídu Firma o všechny třídní atributy (vlastnosti) firmy, o kterých jsme mluvili v úvodu:

class Firma {
    nazev = "Název firmy";
    sidlo = "Praha";
    zamestnanci = [];
}

Název a sídlo jsou jasné. Zaměstnance firmy budeme ukládat do pole, které budeme mít díky výchozí hodnotě po vytvoření instance rovnou připravené.

Zde je důležité si uvědomit, že výraz, jehož hodnota se použije jako výchozí hodnota atributu, se vyhodnocuje při každém vytvoření nové instance.

Díky tomu bude mít každá instance firmy po svém vytvoření vždy unikátní instanci pole ve vlastnosti zamestnanci.

Konstruktor

U všech nově vytvořených instancí firem samozřejmě nechceme mít ve vlastnostech stejné hodnoty. Konkrétní název a sídlo firmy budeme chtít určit hned při vytváření instance. Pomůže nám s tím tzv. konstruktor.

Konstruktor je speciální metoda, která se automaticky zavolá při vytváření nové instance třídy. Slouží k nastavení vnitřního stavu objektu a k provedení případné inicializace.

Konstruktor firmy

Ve třídě budeme potřebovat přistoupit k její aktuální instanci, která se zrovna vytváří, a příslušné vlastnosti ji nastavit. K aktuální instanci ve třídě přistoupíme pomocí klíčového slova this.

Klíčové slovo this se v JavaScriptu bohužel chová různě, v závislosti na kontextu. Je důležité si uvědomit, kde a jak se this používá, aby bylo možné správně určit, na co se odkazuje. K této problematice se v kurzu ještě několikrát vrátíme.

Do třídy Firma si tedy přidáme konstruktor, metodu spouštějící se při vytváření nových instancí. Konstruktor se v JavaScriptu vždy jmenuje constructor():

class Firma {
    nazev = "Název firmy";
    sidlo = "Praha";
    zamestnanci = [];

    constructor() {

    }
}

Inicializaci našich vlastností můžeme klidně přesunout do konstruktoru. Zkusme si to na vlastnostech nazev a sidlo:

class Firma {
    nazev;
    sidlo;
    zamestnanci = [];

    constructor() {
        this.nazev = "Název firmy";
        this.sidlo = "Praha";
    }
}

Vidíme, že k vlastnostem aktuální instance přistupujeme pomocí klíčového slova this.

Vždy však raději upřednostníme nastavení výchozí hodnoty přímo třídnímu atributu, pokud mají mít všechny instance třídy stejnou výchozí hodnotu odpovídající vlastnosti.

Když si nyní zobrazíme naši stránku, dostaneme stejný výsledek jako naposledy:

Tvoje stránka
localhost

Stejného výsledku bychom dosáhli, i kdyby třída Firma vůbec neobsahovala třídní atributy nazev a sidlo:

class Firma {
    zamestnanci = [];

    constructor() {
        this.nazev = "Název firmy";
        this.sidlo = "Praha";
    }
}

Jedná se o způsob, kterým se ve starších verzích JavaScriptu bez třídních atributů vytvářely a inicializovaly vlastnosti.

Použití třídních atributů je však přehlednější a budeme je proto preferovat.

Parametry konstruktoru

Jak již bylo řečeno, nechceme mít u všech firem stejný výchozí název a sídlo. Proto název a sídlo předáme konstruktoru firmy přes jeho parametry. Hodnoty z parametrů následně přiřadíme vlastnostem nazev a sidlo:

class Firma {
    nazev;
    sidlo;
    zamestnanci = [];

    constructor(nazev, sidlo) {
        this.nazev = nazev;
        this.sidlo = sidlo;
    }
}

Parametry konstruktoru následně vyplníme při vytváření instancí naší třídy v souboru obsluha.js:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
const google = new Firma("Google LLC", "Mountain View, California");
document.write(microsoft.nazev);
document.write("<br />");
document.write(google.nazev);

Nyní již víme, k čemu slouží ony závorky za názvem třídy při vytváření její instance :) Po spuštění skriptu získáme stejný výpis, jako v ukázce na začátku lekce:

Tvoje stránka
localhost

Parametry takto samozřejmě můžeme předat jakékoli metodě, jako jsme to v minulosti dělali u funkcí.

Implementace metod

V rámci tréninku firmě přidejme kromě konstruktoru ještě několik klasických metod.

Výpis názvu a sídla

Vytvoříme metodu na vypsání jména naší firmy a jejího sídla. Vyzkoušíme si tak, že k vlastnostem instance můžeme ve třídě jednoduše přistupovat. Vypisovací metoda vypisInfoOFirme() ve třídě Firma může vypadat například takto:

vypisInfoOFirme() {
    document.write("Firma " + this.nazev + " sídlí v " + this.sidlo);
}

V souboru obsluha.js ji zavoláme následovně:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
microsoft.vypisInfoOFirme();

Naše stránka pak bude vypadat takto:

Tvoje stránka
localhost

Vypadá to, že vše funguje velmi dobře :)

Interpolace řetězce

Ovšem vraťme se na chvilku k naší metodě vypisInfoOFirme(). Náš způsob sestavení textu je poněkud nešťastný. Musíme vždy ukončit textový řetězec a poté připojit další. To může být zdrojem chyb a zápis jde samozřejmě zjednodušit.

Do textového řetězce můžeme přímo vkládat nějaké hodnoty, aniž bychom ho museli jakkoli ukončovat. Stačí pro zápis řetězce použít zpětné uvozovky `. Do řetězce pak jednotlivé proměnné vkládáme pomocí dolaru a složených závorek jako ${nazevPromenne}. Tento způsob vkládání hodnot proměnných do řetězců se nazývá interpolace řetězce.

Přepsání metody toString()

Pokud chceme v JavaScriptu získat textovou reprezentaci objektu, použijeme k tomu metodu toString(). Tuto metodu automaticky obsahuje každý objekt, tedy i naše instance firmy.

Zkusme si schválně v souboru obsluha.js vypsat textovou reprezentaci naší firmy:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
document.write(microsoft.toString());

Výsledek:

Tvoje stránka
localhost

Vidíme, že výchozí textovou reprezentací objektu v JavaScriptu je nicneříkající řetězec [object Object]. V naší třídě Firma si však můžeme vytvořit vlastní implementaci metody toString(), takzvaně ji přepsat (někdy se také říká překrýt), a vracet v ní smysluplnější reprezentaci firmy.

K přepisování metod se ještě jednou vrátíme v lekci Dědičnost a polymorfismus.

Odeberme tedy naši metodu vypisInfoOFirme() a nahraďme ji metodou toString(). Využijme zároveň i interpolaci řetězce pro sestavení textové reprezentace firmy:

toString() {
    return `Firma ${this.nazev} sídlí v ${this.sidlo}`;
}

Když si zobrazíme naši stránku, uvidíme očekávaný výpis:

Tvoje stránka
localhost

V souboru obsluha.js metodu toString() dokonce ani nemusíme vůbec volat. Metodě write() můžeme předat přímo náš objekt, na němž si metodu toString() zavolá sama:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
document.write(microsoft);

Nikdy bychom si neměli dělat vlastní metodu, například něco jako náš vypisInfoOFirme(), když máme v JavaScriptu připravenou cestu, jak toto řešit.

Správa zaměstnanců

Dále do třídy přidáme metody na vytváření zaměstnanců a na jejich výpis.

Jelikož budeme pracovat se zaměstnanci, vytvoříme si pro ně novou třídu Zamestnanec v souboru js/Zamestnanec.js, rovnou i s konstruktorem a atributy:

class Zamestnanec {
    jmeno;
    vek;
    pozice;

    constructor(jmeno, vek, pozice) {
        this.jmeno = jmeno;
        this.vek = vek;
        this.pozice = pozice;
    }
}

Soubor s třídou si nezapomeňme opět naimportovat v index.html v tomto pořadí:

<body>
    <script src="js/Zamestnanec.js"></script>
    <script src="js/Firma.js"></script>
    <script src="js/obsluha.js"></script>
</body>

Vytváření zaměstnanců

Pro vytváření zaměstnanců budeme potřebovat získat vstup od uživatele. Jelikož s objekty začínáme, vyhneme se zatím složitému uživatelskému rozhraní (částem aplikace, které interagují s uživatelem pro získání vstupů jako jsou tlačítka, menu, vizuální prvky a další) a použijeme funkci prompt(), která nám pro náš příklad bude stačit.

Nevýhodou dialogového okna prompt() je, že umožňuje zadání pouze jedné hodnoty a nenabízí možnost přizpůsobení. Uživatelsky přívětivějším řešením by bylo si vytvořit vlastní dialogové okno s plnohodnotným formulářem. To je však nad rámec této lekce.

Nová metoda pridejZamestnance() třídy Firma bude vypadat takto:

pridejZamestnance() {
    const jmeno = prompt("Zadej jméno zaměstnance");
    const vek = prompt("Zadej věk");
    const pozice = prompt("Zadej pracovní pozici");
    const zamestnanec = new Zamestnanec(jmeno, vek, pozice);

    this.zamestnanci.push(zamestnanec); // Přidáme nového zaměstnance do pole
}

Na prvních třech řádcích od uživatele načítáme jméno, věk a pracovní pozici nového zaměstnance. Toho dále vytváříme a ukládáme jej do proměnné zamestnanec. Do pole zamestnanci jej přidáváme pomocí metody push().

V souboru obsluha.js naši novou metodu pridejZamestnance() zavoláme:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
document.write(microsoft);
microsoft.pridejZamestnance();

Noví zaměstnanci se zatím nikam nevypisují, pojďme to napravit.

Výpis zaměstnanců

Do třídy Firma přidáme poslední metodu vypisZamestnance(), která do stránky vypíše HTML seznam našich zaměstnanců. Vypadat bude takto:

vypisZamestnance() {
    document.write("<h2>Zaměstnanci</h2>");
    const seznam = document.createElement("ul");
    for (const zamestnanec of this.zamestnanci) {
        seznam.innerHTML += `
            <li>
                <h3>${zamestnanec.jmeno}</h3>
                <dl>
                    <dt>Věk:</dt>
                    <dd>${zamestnanec.vek}</dd>
                    <dt>Pozice:</dt>
                    <dd>${zamestnanec.pozice}</dd>
                </dl>
            </li>`;
    }
    document.body.appendChild(seznam);
}

Nejprve na stránku vypisujeme nadpis Zaměstnanci. Následně sestavujeme samotný seznam jako element typu <ul>, tedy nečíslovaný seznam. Procházíme naše pole zaměstnanců cyklem a informace každého zaměstnance vkládáme jako odrážky do seznamu. Jméno zaměstnance vypisujeme do nadpisu a zbylé vlastnosti do seznamu typu <dl>, který se skvěle hodí pro zobrazení dat tvaru klíč-hodnota. Nakonec náš nečíslovaný seznam vkládáme do elementu <body>.

Nyní již stačí metodu vypisZamestnance() zavolat v souboru obsluha.js. Ještě můžeme zavolat podruhé metodu na vytvoření dalšího zaměstnance, abychom viděli, jak bude náš seznam vypadat s více zaměstnanci:

const microsoft = new Firma("Microsoft Corporation", "Redmont, Washington");
document.write(microsoft)
microsoft.pridejZamestnance();
microsoft.pridejZamestnance();
microsoft.vypisZamestnance();

Když si naši stránku zobrazíme v prohlížeči a vyplníme informace o zaměstnancích, bude stránka vypadat takto:

Tvoje stránka
localhost

V následujícím cvičení, Řešené úlohy k 1.-3. lekci OOP v JavaScriptu, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 718x (2.79 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

Předchozí článek
První objektová aplikace v JavaScriptu
Všechny články v sekci
Objektově orientované programování v JavaScriptu
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 1.-3. lekci OOP v JavaScriptu
Článek pro vás napsal Šimon Raichl
Avatar
Uživatelské hodnocení:
332 hlasů
Autor se věnuje především vývoji v JavaScriptu
Aktivity