Objektově orientované programování v JavaScriptu

6. listopadu 2003

Pokud píšete v JavaScriptu jednodušší aplikace, postačí použít obyčejné strukturované programování a jednoúrovňové definice potřebných funkcí. Ovšem ne vždy může být takovýto styl optimální a především u rozsáhlejších programů zjistíte, že kód je stále nepřehlednější a logika zápisu poněkud pokulhává…

Ne nadarmo tedy už jistou řádku let existuje objektově orientované programování (OOP), známé především z C/C++ nebo jazyků Simula a Beta. I JavaScript zná pojmy OOP jako jsou instance, konstruktor, metoda či dědičnost, byť to není v pravém slova smyslu objektový jazyk. V následujícím článku vám povím něco o základech OOP v JS.

Objekty a třídy

Objektem, tak jako u jiných jazyků, míníme určitý soubor vlastností v jednom složeném datovém typu. Vlastnostmi daného objektu mohou být jak datové složky (proměnné), tak procedury, zvané metody. Chápání pojmu objekt v programování je zcela identické jako u objektů v reálném životě. Existuje-li nějaká konkrétní osoba, má své specifické datové složky, například věk, pohlaví a podobně. Umí-li dotyčná osoba sčítat, její metodou je secti(a,b).

Nejprve budu hovořit o objektech, jejichž charakteristickou vlastností je singularita, jinými slovy takový typ, který se v programu vyskytuje pouze jednou. Chceme-li pracovat s objektem Země, pravděpodobně žádná další existovat nebude (alespoň zatím). Takovýto objekt můžeme vytvořit skrze literál objektu, jehož syntaxi můžete vidět níže. Výčtem se definují a inicializují jak proměnné objektu (pocet_lidi), tak metody (otoc_doprava), a při interpretaci JS dojde k okamžitému vytvoření požadovaného objektu. Všimněte si, že metody lze definovat jak přímo v literálu (otoc_doleva), tak i mimo něj (otoc_doprava). Přístup k jednotlivým vlastnostem je identický jako u objektů DOMu (tečková konvence).

function otoc() { alert(‚Otacim doleva‘); }
// Literal objektu
var zeme = {
  pocet_lidi:“6 000 000 000″,
  kontinenty : new Array(‚Amerika‘, ‚Asie‘),
  otoc_doleva: otoc,
  otoc_doprava: new Function(„alert(‚Otacim do prava)“)
};
// Vypise pocet lidi
alert(zeme.pocet_lidi);
// Otoci zemi doleva
zeme.otoc_doleva();

S podobným zápisem se můžete setkat u detektorů typu prohlížeče, které definují nový objekt browser s logickými proměnnými reprezentujícími jednotlivé prohlížeče:

var browser = {
  IE:(document.all)? true:false,
  DOM:(document.getElementById)? true:false,
  NS4:(document.layers)? true:false
};
// Je tento prohlizec NS4?
alert(browser.NS4);

Častější jsou ale případy, kdy je potřeba popsat určitý výsek reality, obsahujíc větší počet samostatných objektů stejného typu. Literály jsou jednocestná definice konkrétního objektu , provádějící se pouze při interpretaci JavaScriptu, tudíž tudy cesta nevede. Navíc přidávání nových objektů za běhu programu by bylo nemožné. V takovýchto případech využíváme definice vlastní struktury nového objektu, kterou lze nazvat třídou. Slovíčko „lze“ jsem použil záměrně, neboť se nejedná o definici třídy, tak ji známe např. z C++, ale přímo o definici inicializační funkce, takzvaného konstruktoru. Voláním konstruktoru pomocí operátoru new se pak vytváří kýžené objekty, označované jako instance. V níže uvedeném zápise si také povšimněte, že vlastnost objektu se deklaruje přes klíčové slovo this. Přímo při vytváření objektu lze přejímáním parametrů volaného konstruktoru inicializovat jednotlivé proměnné.

// Definice konstruktoru
function uzivatel(jmeno, prijmeni) {
 this.jmeno = jmeno;
 this.prijmeni = prijmeni;
 this.vypisJmeno() = new Function(„alert(this.jmeno);“);
}
// Vytvoření nového uzivatele
novy_uzivatel = new uzivatel(„michal“, „kusyn“);

Dědičnost (inheritance)

Ačkoliv má JavaScript určité prvky dědičnosti, nejedná se o klasickou dědičnost známou z typových jazyků C/C++. Spíše můžeme mluvit o simulaci, neb JavaScript nepodporuje zápis tříd (pouze konstruktor) a s tím spojenou hierarchii tříd. Inheritance je u JavaScriptu tzv. prototypová. Prototyp je zvláštní objekt, který je přidružen každé funkci, tedy i k funkci konstruktoru. Všechny objekty inicializované skrze nějaký konstruktor dědí, respektive sdílejí, vlastnosti a metody obsažené v prototypu daného konstruktoru.

Podíváte-li se zpět na zápis konstruktoru uzivatel, v objektu je vložená také metoda vypis() Technicky má v paměti každá instance této „třídy“ lokální kopii této metody, což je přinejmenším z hlediska správy paměti nevhodné. Právě tento problém řeší prototypy, neboť stačí metodu deklarovat na úrovni prototypu a ta již nebude součástí každé instance. To ovšem neznamená, že nebude dosažitelná. Při vyhodnocování JavaScript hledá metodu nejprve v příslušném objektu, pakliže ji nenajde, jde o úroveň výše – do prototypu konstruktoru. Samozřejmě je vhodné sdílení pouze takových datových složek, které jsou neměnné pro všechny instance. To znamená především metody a konstanty. Jak by vypadal upravený zápis předchozího příkladu můžete vidět níže:

// Definice konstruktoru
function uzivatel(jmeno, prijmeni) {
 this.jmeno = jmeno;
 this.prijmeni = prijmeni;
}
uzivatel.prototype.vypisJmeno = new Function(„alert(this.jmeno);“);

Při hledání metod postupuje JS až k nejvyššímu nadřazenému objektu. Tím je obecný nadtyp Object, zastřešující všechny ostatní objekty. Demonstrací předchozích tvrzení je konstrukce Object.prototype.server = "Interval.cz", která všem již existujícím nebo v budoucnu vytvořeným objektům „přiřadí“ vlastnost server s příslušnou hodnotou. Nadtyp Object lze využít i k další možné definici singulárních objektů. Diky tomu, že JS není přísně typový jazyk a vlastnosti můžeme přidávat dynamicky, lze objekty vytvářet i následujícím způsobem:

// Definice singularniho objektu zeme
zeme = new Object();
zeme.pocet_lidi = 6 000 000;
zeme.kontinenty = new Array(‚Amerika‘, ‚Evropa‘);
zeme.otoc_doprava = new Function(„alert(‚otacim do prava‘);“);

Systém dědičnosti nadřízených a podřízených tříd sice v JS neexistuje, ale pomocí prototypů ji lze úspěšně simulovat. Pravda, jedná se již o extrémní případ, ale jde to. Celý princip dědění spočívá v tvorbě nových prototypů. Máme-li třídu vesmirny_objekt, třídu planeta podřídíme tím, že přiřadíme konstruktoru planeta jako prototyp objekt vesmirny_objekt. Nevýhodou je fakt, že tímto způsobem nelze inicializovat hodnoty v nadřazené třídě prostřednictvím parametrů.

function vesmirny_objekt()
{
  this.soustava = „slunecni“;
}
function planeta(pocet)
{
  this.pocet_mesicu = pocet
}
planeta.prototype = new vesmirny_objekt();
zeme = new planeta(3);
alert(zeme.pocet_mesicu);
alert(zeme.soustava);

Objektové programování a DHTML

Nyní se konečně dostávám k využití OOP v DHTML aplikacích. Veškeré teoretické základy, uvedené výše, lze zužitkovat v zcela jiný styl programování. Ten spojí úroveň dokumentu a dat v jeden JavaScriptový objekt. Nespornou výhodou vytváření takovýchto homogenních prvků je přehlednější logika aplikace, spojení společných vlastností a metod v jeden celek, rozšiřitelnost a snadná úprava kódu. Nároky jsou hlavně při návrhu a realizaci jednotlivých objektů jistě vyšší, ale základní poučka OOP zní, že čím lépe a pečlivěji navrhnete systém tříd a jejich vlastností, tím snadněji lze realizovat běh programu.

Předpokládejme, že modelujeme situaci jisté databáze bookmarků na internetu. Databáze bude umět přidat nový odkaz a smazat vybrané odkazy. Databázi budeme považovat za singulární, zhotovíme ji výčtem. Odkazy naopak definujeme jako instance třídy. Důležitým faktorem je provázání s HTML dokumentem, se kterým databáze při výpisu uživatelů pracuje. Ostatní metody jsou již rutinou a provádějí činnosti odpovídající jejich názvu. Návrh realizace naleznete v dokumentu databaze.htm. Ukládáním dat se tentokrát zabývat nebudeme.

Výjimky

Poslední věcí, o které bych se rád zmínil, jsou výjimky. Ač to přímo nesouvisí s DHTML, toto téma je známé z objektově orientovaných jazyků, takže jen krátce předvedu podstatu věci. Jedná se o mechanismus zachytávání stavů, vyvolaných chybným kódem nebo, například, přístupem k neexistují metodě. Tyto chyby lze zachytávat pomocí klíčových slov try, catch a volitelně finaly. Jejich syntaxe a popis je uvedena níže. Když jsem v úvodním díle o DHTML tajemně naznačil další způsob ošetřování různé podpory DOM, měl jsem na mysli právě zachytávání výjimek. Následující konstrukce u prohlížečů znající metodu document.implementation nevyvolá žádnou reakci, zatímco u ostatních se zobrazí chybová hláška.

try {
// V tomto bloku se provadi kod ve kterem testujeme, zda-li nedojde k vyjimkce
s = document.implementation.hasFeature(‚HTML‘,’1.0′)
}
catch(e) {
// Tento blok obsauje kod, ktery se spusti, jestlize dojde v bloku try k vyjimkce
alert(‚Doslo k chybe. Vas prohlizec pravdepodobne nepodporuje nekterou z pouzivanych metod.‘);
}
finally {
// Kod bloku Finaly se vykona vzdy, at dojde k vyjimce nebo ne
}

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Předchozí článek Nové přechody - přehled 5.
Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *