Vianoce v ITnetwork sú tu! Dobí si teraz kredity a získaj až 80 % extra kreditov na e-learningové kurzy ZADARMO. Zisti viac.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

4. diel - Referenčnej a hodnotové dátové typy v JavaScripte

V predchádzajúcom cvičení, Riešené úlohy k 1.-3. lekciu OOP v JavaScripte, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 1.-3. lekciu OOP v JavaScripte , sme si vytvorili svoj prvý poriadny objekt, bola ním firma. Začíname pracovať s objektmi a objekty sú referenčnými dátovými typy, ktoré sa v niektorých ohľadoch správajú inak, než typy hodnotové (napr. Čísla). Je dôležité, aby sme presne vedeli, čo sa vo vnútri programu deje, inak by nás v budúcnosti mohlo všeličo prekvapiť.

Hodnotové a referenčný dátové typy

Hodnotové dátové typy sú väčšinou jednoduché štruktúry, napr. Jedno číslo. Väčšinou sa chce, aby sme s nimi pracovali čo najrýchlejšie, v programe sa ich vyskytuje veľmi veľa a zaberajú málo miesta. V anglickej literatúre sú často opisované slovami light-weight. Príkladom sú premenné typu number alebo boolean.

Aplikácia má operačným systémom pridelenú pamäť v podobe tzv. Zásobníka (stack). Jedná sa o veľmi rýchlu pamäť s priamym prístupom, jej veľkosť aplikácie nemôže ovplyvniť, prostriedky sú prideľované operačným systémom. Zjednodušene môžeme povedať, že táto malá a rýchla pamäť je využívaná na ukladanie lokálnych premenných hodnotového typu, aj keď to JS štandard nijako neprikazuje a v praxi by sa o tejto problematike dala napísať kniha. Hodnotové typy sú v JavaScripte niekedy synonymum pre tzv. Immutable objekty. Ale zatiaľ sa týmito vecami nebudeme trápiť :)

Premennú hodnotového typu si v pamäti môžeme predstaviť asi takto:

Premenná hodnotového typu v pamäti stack v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Na obrázku je znázornená pamäť, ktorú môže naše aplikácie využívať. V aplikácii sme si vytvorili premennú a, ktoré JavaScript priradil typ number. Jej hodnota je 56 a uložila sa nám priamo do zásobníka. Kód by mohol vyzerať takto:

let a = 56;

Môžeme to chápať tak, že premenná a má pridelenú časť pamäte v zásobníku (veľkosti dátového typu number, teda 64 bitov), v ktorej je uložená hodnota 56.

Vytvorme si novú jednoduchú triedu, ktorá bude reprezentovať užívateľa nejakého systému:

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

    vratUzivatele() {
        return `${this.jmeno} (${this.vek})`;
    }
}

Trieda má 2 jednoduché vlastnosti, konštruktor a metódu vratUzivatele(), aby sme používateľa mohli jednoducho vypisovať. V nejakom ďalšom skriptu, napr. Opäť v obsluha.js, vytvoríme inštanciu tejto triedy:

let a = 56;
let u = new Uzivatel("Jan Novák", 28);

Premenná u s užívateľom je teraz referenčného typu. Na uloženie inštancie sme tentoraz použili let namiesto const, pretože hodnotu premennej budeme za okamih meniť, na uloženie premenné to nemá žiadny vplyv. Pozrime sa na novú situáciu v pamäti:

Referenčný typ na halde v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Vidíme, že objekt (premenná referenčného dátového typu) sa už neukladá do zásobníka, ale do pamäte zvanej halda. Je to z toho dôvodu, že objekt je spravidla zložitejšie ako hodnotový dátový typ (väčšinou obsahuje hneď niekoľko ďalších atribútov) a tiež zaberá v pamäti viac miesta.

Zásobník aj halda sa nachádzajú v pamäti RAM.

Premenné referenčného typu sú v pamäti uložené vlastne na dvakrát, raz v zásobníku a raz v halde. V zásobníku je uložená iba tzv. Referencie, teda odkaz do haldy, kde sa potom nachádza naozajstný objekt.

Môžete sa pýtať, prečo je to takto urobené. Dôvodov je hneď niekoľko, poďme si niektoré vymenovať:

  1. Miesto vo stacku je obmedzené.
  2. Keď budeme chcieť použiť objekt viackrát (napr. Ho odovzdať ako parameter do niekoľkých metód), nemusíme ho v programe odovzdávať ako kópiu. Odovzdáme iba malý hodnotový typ s referenciou na objekt namiesto toho, aby sme všeobecne pamäťovo náročný objekt kopírovali. Toto si vzápätí ukážeme.
  3. Pomocou referencií môžeme jednoducho vytvárať štruktúry s dynamickou veľkosťou, napr. Štruktúry podobné poli, do ktorých môžeme za behu vkladať nové prvky. Tie sú na seba navzájom odkazované referenciami, ako reťaz objektov.

Založme si 2 číselné premenné a dve inštancie triedy Uzivatel:

let a = 56;
let b = 28;
let u = new Uzivatel("Jan Novák", 28);
let v = new Uzivatel("Josef Nový", 32);

Situácia v pamäti bude nasledovné:

Referencie na haldu v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Teraz skúsme priradiť do premennej a premennú b. Rovnako tak priradíme aj premennú v do premennej u. Hodnotový typ sa v zásobníku len skopíruje, pri objekte sa skopíruje iba referencie (čo je vlastne tiež hodnotový typ), ale objekt máme stále len jeden. V kóde vykonáme teda toto:

let a = 56;
let b = 28;
let u = new Uzivatel("Jan Novák", 28);
let v = new Uzivatel("Josef Nový", 32);
// Přiřazení
a = b;
u = v;

V pamäti bude celá situácia vyzerať nasledovne:

Ukladanie referencií v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Presvedčte sa o tom, aby ste videli, že to naozaj tak je :) Najprv si necháme všetky štyri premenné vypísať pred a po zmene. Pretože budeme výpis premenných volať viackrát, napíšeme si pre neho v obsluha.js jednoduchú funkciu. Tentoraz ich budeme vypisovať do konzoly do prehľadnej tabuľky, kde bude na každom riadku jedna premenná a v stĺpcoch jej hodnoty. Upravme teda kód na nasledujúce:

function vypisPromenne() {
    console.table({
            "a": a,
            "b": b,
            "u": u,
            "v": v
        });
}

let a = 56;
let b = 28;
let u = new Uzivatel("Jan Novák", 28);
let v = new Uzivatel("Josef Nový", 32);

vypisPromenne();

// Přiřazení
a = b;
u = v;

vypisPromenne();

Po spustení v prehliadači si otvorte vývojársku konzolu pomocou F12, potom kliknite na záložku Console, kde bude podobná tabuľka:

Debug výpis v podobe tabuľky do konzoly - Objektovo orientované programovanie v JavaScriptu

Na výstupe programu zatiaľ rozdiel medzi hodnotovým a referenčným typom nepoznáme. Avšak vieme, že kým v a a b sú naozaj 2 rôzne čísla s rovnakou hodnotou, v u a v je ten istý objekt. Poďme zmeniť meno používateľa v a podľa našich predpokladov by sa mala zmena prejaviť aj v premennej u. Na obsluhu pripíšeme:

v.jmeno = "John Doe";

vypisPromenne();

Zmenili sme objekt v premennej v a znovu vypíšeme u a v:

Referencie v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Spolu so zmenou v sa zmení aj u, pretože premenné ukazujú na ten istý objekt. Ak sa pýtate, ako vytvoriť naozajstnú kópiu objektu, tak najjednoduchšie je objekt znova vytvoriť pomocou konstruktoru a dať do neho rovnaké dáta. Pripomeňme si situáciu v pamäti ešte raz a zamerajme sa na Jána Nováka.

Garbage Collector v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Čo sa sním stane? "Zožerie" ho tzv. Garbage collector.

Garbage Collector - Objektovo orientované programovanie v JavaScriptu

Garbage collector a dynamická správa pamäte

Pamäť môžeme v programoch alokovať staticky, to znamená, že v zdrojovom kóde vopred určíme, koľko jej budeme používať. Doteraz sme to tak vlastne robili a nemali sme s tým problém, pekne sme do zdrojového kódu napísali potrebné premenné. Čoskoro sa ale budeme stretávať s aplikáciami, kedy nebudeme pred spustením presne vedieť, koľko pamäte budeme potrebovať. V tomto prípade hovoríme o dynamickej správe pamäte.

V minulosti, hlavne v časoch jazykov C, Pascal a C ++, sa na tento účel používali tzv. Pointer, čiže priame ukazovatele do pamäte. Napospol to fungovalo tak, že sme si povedali operačnému systému o kus pamäti o určitej veľkosti. On ju pre nás vyhradil a dal nám jej adresu. Na toto miesto v pamäti sme mali pointer, cez ktorý sme s pamäťou pracovali. Problém bol, že nikto nestrážil, čo do pamäti dávame (ukazovateľ smeroval na začiatok vyhradeného priestoru). Keď sme tam dali niečo väčšieho, skrátka sa to rovnako uložilo a prepísala sa dáta za naším priestorom, ktorá patrila napríklad inému programu alebo operačnému systému (v tom prípade by našu aplikáciu OS asi zabil - zastavil). Často sme si však my v pamäti prepísali nejaká ďalšie dáta nášho programu a program sa začal správať chaoticky. Predstavte si, že si uložíte používateľa do poľa a v tej chvíli sa vám zrazu zmení farba užívateľského prostredia, teda niečo, čo s tým vôbec nesúvisí. Hodiny strávite tým, že kontrolujete kód pre zmenu farby, potom zistíte, že je chyba v založenia používateľa, kedy dôjde k pretečeniu pamäte a prepísanie hodnôt farby.

Môj kolega raz povedal: "Ľudský mozog sa nedokáže starať ani o správu pamäte vlastné, nieto aby riešil memory management programu." Mal samozrejme pravdu, až na malú skupinu géniov ľudí prestalo baviť riešiť neustále a nezmyselné chyby. Za cenu mierneho zníženia výkonu vznikli riadené jazyky (managed) s tzv. Garbage collector, jedným z nich je aj JavaScript.

garbage collector - Objektovo orientované programovanie v JavaScriptu
Garbage collector je vlastne program, ktorý beží paralelne s našou aplikáciou, v samostatnom vlákne. Občas sa spustí a pozrie sa, na ktoré objekty už v pamäti nevedú žiadne referencie. Tie potom odstráni. Strata výkonu je minimálna a značne to zníži percento samovrážd programátorov, ladiacich po večeroch rozbité pointera. Pretože je jazyk riadený a nepracujeme s priamymi Pointer, nie je vôbec možné pamäť nejako narušiť, nechať ju pretiecť a podobne, interpret sa o pamäť automaticky stará.

Hodnota null a undefined

Posledná vec, o ktorej sa dnes zmienime, sú hodnoty null a undefined.

Null

Kľúčové slovo null označuje, že referencie neukazuje na žiadne dáta. Keď nastavíme premennú v na null, zrušíme iba tú jednu referenciu. Pokiaľ na náš objekt existuje ešte nejaká referencie, bude aj naďalej existovať. Ak nie, bude uvoľnený GC. Zmeňme ešte posledná riadky nášho programu na:

v.jmeno = "John Doe";
u = null;

vypisPromenne();

dostaneme:

Hodnota null v JavaScripte - Objektovo orientované programovanie v JavaScriptu

Vidíme, že objekt stále existuje a ukazuje na neho premenná u, v premennej v už nie je referencie.

Undefined

Teraz k undefined. Keď si vytvoríme nejakú premennú, ktoré však nenastavíte hodnotu, bude v nej práve undefined. Skúsme si to:

let promenna;
console.log(promenna); // vypíše undefined

Táto hodnota je tiež napr. Na indexoch polí, na ktorých zatiaľ nie je žiadna hodnota. Označuje teda, že premenná existuje, ale nič neobsahuje. To isté by sa stalo, ak by sme vytvorili nejakú funkciu s parametrom a parameter pri volaní nezadali:

function zkouska(parametr) {
    console.log(parametr);
}

zkouska(); // vypíše undefined

null na rozdiel od undefined označuje, že do premennej už bolo uložených a bola nastavená práve na prázdnu referenciu.

V budúcej lekcii, Tvorba OOP diáre v JavaScripte , si zas niečo praktické naprogramujeme, nech si vedomosti zažijeme. Prezradím, že pôjde o elektronický diár :)


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 183x (2.13 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Riešené úlohy k 1.-3. lekciu OOP v JavaScripte
Všetky články v sekcii
Objektovo orientované programovanie v JavaScriptu
Preskočiť článok
(neodporúčame)
Tvorba OOP diáre v JavaScripte
Článok pre vás napísal Šimon Raichl
Avatar
Užívateľské hodnotenie:
2 hlasov
Autor se věnuje hlavně tvorbě všemožných věcí v JS
Aktivity