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:
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:
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:
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:
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:
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
:
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:
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:
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:
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:
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:
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:
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