Rychlý průvodce pokročilým JavaScriptem

25. února 2010

JavaScript se v průběhu let rozvíjel a začleňoval do sebe další technologie. Nejdůležitější z nich (DOM, OOP, regEx, AJAX, canvas) si nyní představíme.

Začátky a DOM

Velmi brzy po rozjezdu WWW se objevila potřeba technologií, které by webovým stránkám umožnily interakci s uživateli. První inovací v tomto směru bylo automatické generování stránek prostřednictvím rozhraní CGI (Common Gateway Interface). Jednou z hlavních nevýhod tohoto řešení byla pomalá odezva CGI skriptů, proto se začalo přemýšlet, jak by se daly alespoň některé úlohy přesunout ze serveru na klienta, což by znamenalo značné urychlení. Kýžená spása se dostavila ve dvou variantách. Tou první byl nový jazyk Java, představený firmou Sun Microsystems, který umožňoval psaní Java apletů – prográmků integrovaných přímo do stránky. Tou druhou byl v roce 1996 JavaScript.

Možnosti JavaScriptu nebyly v původní implementaci od firmy Netscape závratně rozsáhlé, to se však změnilo v roce 1997 s nástupem IE 4. V novém prohlížeči od Microsoftu již bylo možno přistupovat ke každému objektu na stránce a měnit jej (tzv. DHTML). Konkurenční Netscape brzy vyvinul podobné API. Jelikož podobné neznamená stejné, museli programátoři vytvářet skripty složitěji, nebo je psát jen pro jeden z prohlížečů. Situaci vyřešilo až W3C, které vypracovalo DOM (Document Object Model) a zavedlo jej jako standard. Specifikovalo v něm rozhraní pro přístup k objektům dokumentu a událostem. Jen to samozřejmě nestačí, a tak je DOM doplňován dalšími API, z nichž většinu dokumentuje připravovaná specifikace HTML 5 (mezi nejvýznamnější z nich patří např. drag and drop API, Canvas nebo API pro přehrávání multimediálních objektů.

O DOM a DHTML na Interval.cz

Objektově orientované programování

Při vytváření větších aplikací, na kterých se podílí více programátorů, je OOP nevyhnutelnou nutností, proto nesmí chybět ani v JavaScriptu. Nejprve si ukážeme, jakými způsoby se v JavaScriptu objekty vytvářejí.

var automobil = new Object();
automobil.barva = "modrá";
automobil.rok_vyroby = 2003;

automobil.nastartuj = function() {
  this.zvuk = "vrumm";
  this.stav = "nastartováno";
}

V prvním příkladu jsme pomocí operátoru newdefinovali objekt automobil, kterému jsme vzápětí přiřadili pár vlastností a jednu metodu.

var automobil = {
  barva : "modrá",
  rok_vyroby : 2003,
  nastartuj : function() {alert("Vrumm")}
}

Ve druhém příkladu jsme pro vytvoření objektu automobil využili objektový literál. Výsledek je ovšem v podstatě stejný, liší se jen metoda nastartuj(), poněvadž v každém příkladu obsahuje jiné příkazy.

Na předchozích dvou příkladech jsme si představili dva poměrně jednoduché způsoby vytváření objektů. Ani jeden z nich se však příliš nehodí v situaci, kdy je potřeba vytvářet více objektů stejné povahy – tedy se stejnými vlastnostmi a metodami. Proto je tu ještě jiné řešení – konstruktor. Na dalším příkladu si ukážeme, jak takový objektový konstruktor vypadá. Zdánlivě jako běžná funkce.

function automobil(barva) {
  this.zbarveni = barva;
  this.rok_vyroby = 2001;
  this.nastartuj = function() {(alert("Vrumm"))}
}

var auto1 = new automobil("černé");
var auto2 = new automobil("modré");
var auto3 = new automobil("červené");

alert("Mám krásné " + auto1.zbarveni + " auto z roku " + auto1.rok_vyroby + ".");
/* A to auto dělá... */ auto1.nastartuj();

Konstruktor se volá s operátorem new. Každý objekt stvořený (inicializovaný) skrze konstruktor je instancí svého konstruktoru.

Výhoda právě popsaného řešení je z příkladu asi zřejmá. Inicializovali jsme objekty (instance) auto1, auto2, auto3. Všechny mají vlastnosti zbarveni, rok_vyroby a metodu nastartuj(), aniž bychom to explicitně přiřazovali pro každou instanci zvlášť, takže jsme si ušetřili hromadu psaní. Ať žije konstruktor!

Nyní si povíme něco o dědičnosti (inheritance). Ta v JavaScriptu zatím neexistuje. Alespoň ne v té klasické podobě, jak ji známe z jiných jazyků, např. z C++. Existuje zde ale jakási prototypová dědičnost. 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 svého konstruktoru, což se dá skvěle využít, když je potřeba přidat již definovanému konstruktoru nějakou novou vlastnost či metodu.

function automobil(barva) {
  this.zbarveni = barva;
  this.rok_vyroby = 2001;
}

automobil.prototype.nastartuj = function() {(alert("Vrumm"))}
var auto1 = new automobil("černé");
auto1.nastartuj(); //vyhodí alert s hláškou "Vrumm"

Prototyp lze stejně dobře využít i na předdefinované objekty (konstruktory) jako Array(), Object(), String() apod.

Regulární výrazy

Regulární výrazy (regular expressions, zkr. RE) znamenaly v programovacích jazycích nesmírný přínos pro práci s řetězci. Umožnily řetězce porovnávat s obecnými vzory a tím pomohly výrazně zkrátit mnohé úlohy. Bez regulárních výrazů se dnes programátoři prakticky neobejdou. Tato prostá a přitom elegantní technika má široké možnosti uplatnění. Jsou užitečné zejména při validaci (ověřování formální správnosti) vstupních dat, neboť umožňují výrazně snížit množství přijatých údajů nevhodného tvaru.

Následující tabulka obsahuje seznam znaků se speciálním významem.

Znak Význam
^ hledá shodu jen na začátku
$ hledá shodu na konci
* libovolný počet opakování (i 0)
+ opakování (1 a vícekrát)
? volitelný výskyt (0 nebo 1krát)
{x} přesně x výskytů
{x,} alespoň x výskytů
{x,y} alespoň x, ale ne více než y výskytů
() sekvence znaků v závorkách se uloží do paměti a dále se s nimi pracuje jako s jedním výrazem
[] znak, který odpovídá některému ze znaků nebo znaku z rozsahů uvedených v závorkách. Příklady zápisu: [FuRz], [5-9], nebo [FuRz5-9]
[^] opak předchozího; každý znak, který neodpovídá ničemu v závorce. Příklad zápisu: [^A-Z]
. libovolný znak, kromě znaku nového řádku
\ zrušení speciálního významu zvláštního znaku nebo přiřazení zvláštního významu běžnému znaku
[\b] znak „backspace“
\b znak na hranici slova, před nebo za bílým místem (whitespace), který není zahrnut do podřetězce vyhovujícího regulárnímu výrazu; pro shodu s počátkem slova se umístí před znak (\bx), pro shodu s koncem za něj (x\b)
\B vnitřní část slova
\d dekadická číslice
\D opak dekadické číslice
\f znak „form feed“
\n znak „new line“
\r znak „carriage return“
\s bílé místo (tj. mezera, tabulátor, nový řádek, …)
\S opak bílého místa
\t znak tabulátor
\v znak vertikální tabulátor
\w písmeno, číslice, nebo podtržítko
\W cokoliv s výjimkou písmen, číslic a podtržítka

Pokud si chcete otestovat vaši znalost regulárních výrazů, zde je k dispozici malý test.

Využití pro testování výskytu

Ke zjištění, zda se v určitém řetězci nachází podřetězec odpovídající danému RE, ideálně poslouží metoda test() objektu RegExp. Jejím argumentem je testovaný řetězec. Vrací hodnotu typu boolean. Následující příklad analyzuje akademický titul.

var titul = window.prompt("Napište své jméno (i s případným titulem).");
var diak = "áäčďéěíĺľňóôőöŕšťúůűüýřž";
var vzor = new RegExp("Dr[^a-z" + diak + "]");
var isDoctor = vzor.test(titul);
if (isDoctor) window.alert("Óóó, Vy jste doktor.");

Další příklady využití naleznete v tomto článku: jak využít regulárních výrazů.

JSON (JavaScript Object Notation)

JSON (JavaScript Object Notation) je univerzální datový formát, který využívá konvence známých programovacích jazyků a díky tomu jimi může být podporován. Je založený na dvou strukturách:

  • Kolekce párů klíč/hodnota. Ta bývá v rozličných jazycích nazývána jako objekt, záznam (record), struktura (struct), slovník (dictionary), hash tabulka, klíčový seznam (keyed list) nebo asociativní pole.
  • Tříděný seznam hodnot. Ten je ve většině jazyků nazýván jako pole, vektor, seznam (list) nebo posloupnost (sequence).

Objekt je netříděná množina párů klíč/hodnota (key/value). Objekt je uvozený znakem { a zakončený znakem }, je tedy ohraničený složenými závorkami. Každý klíč je následovaný dvojtečkou a jednotlivé páry klíč/hodnota jsou od sebe oddělené čárkou. Příklad:

{"Jmeno" : "Petr Kovář", "Vek" : 21, "Svobodny" : true, "Majetek" : null}

Pole je setříděnou kolekcí hodnot. Začíná znakem [ a končí znakem ]. Hodnoty jsou oddělené čárkou. Příklad:

["voda", "vápno", "cement", "písek"]

Klíč má charakter řetězce a jako takový musí být uzavřen v uvozovkách. Hodnotou může být řetězec, číslo nebo výrazy true, false, null. Hodnotou může být dokonce i objekt nebo pole, vznikají tak vnořené struktury, předvedeme si to na dalším příkladu:

var JSONObject = {"string" : [
  "number", "string", null, [
    true, false
    ]
  ]
};

Data do JSON obvykle nedáváme za účelem jejich pohřbení, nýbrž proto, abychom s nimi mohli později dále pracovat. Jak k nim však v JavaScriptu získat přístup? Postup je přesně stejný jako u každého jiného objektu a pole.

// v dialogovém okně vypíše hodnotu vlastnosti nazev_klice objektu nazev_objektu
alert(nazev_objektu.nazev_klice);
// v dialogovém okně vypíše třetí prvek pole nazev_pole
alert(nazev_pole[2]);

Tento snadný přístup k datům je důvodem, proč se v AJAXu tak často využívá jako formát pro strukturovaná data JSON místo XML.

AJAX (Asynchronous JavaScript and XML)

V případě, že je potřeba obměnit jenom část obsahu stránky, je zbytečné ji znovu celou načítat. Uživatel by musel déle čekat a taky by to bylo plýtvání přenosovou kapacitou. Iframe (stránka ve stránce) byl důvtipným řešením, které se i dnes hodně používá, avšak našlo se řešení ještě důmyslnější – AJAX. Funguje jednoduše: jsou-li potřeba nová data, odešle se prostřednictvím JavaScriptu HTTP požadavek na server, obdržená data se (opět JavaScriptem) zpracují a vloží do stránky.

Zpracování došlých dat souvisí s jejich formátem, například text či fragment HTML kódu se jen vloží do stránky pomocí vlastnosti innerHTML, naopak v případě XML je možné procházet stromovou strukturu a pracovat přímo s jednotlivými uzly. Údaje formátu JSON lze nejsnadněji získat funkcí eval():

var myObject = eval('(' + http_request.responseText + ')');

V tomto způsobu ovšem spočívá jisté bezpečnostní riziko. Programátor, který jej použije, je povinen zajistit, že se do stahovaných dat nevloudí škodlivý kód! Jelikož však na programátory, stejně jako na ostatní příslušníky lidského rodu, není spolehnutí, bude zřejmě nezbytné přijít v další verzi JavaScriptu s JSON parserem, který bude jistě obezřetnější.

U některých webových prezentací využívajících AJAX bývá zhola nemožné dostat se použitím tlačítka Zpět k předchozímu obsahu dané prezentace nebo vytvořit odkaz na jiný než její výchozí obsah. Lze to napravit tím, že se každému jednotlivému obsahu přidělí tzv. kotva, takže každý obsah bude mít svou adresu (např.: http://priklad.cz/ajax.html#cast3), na níž bude možno odkázat a podle té části za mřížkou se pak vygeneruje žádaný obsah.

Canvas

Dynamické generování obrázků PHP skriptem má své drobné nevýhody – zatěžování serveru, pomalejší odezvu atd. Je proto vhodnější v případech, kdy se obrázky generují na základě uživatelem dodaných parametrů, dát přednost JavaScriptu.

Element canvas představuje plochu o výchozích rozměrech 300 × 150 pixelů, na níž lze JavaScriptem kreslit různé geometrické tvary – čáry, kruhy, obdélníky atd. – a sestavovat z nich jednoduché obrázky.

Kód pro Canvas je vhodné podmínit, protože zastaralé prohlížeče Canvas nepodporují a provádění skriptu by v nich skončilo chybou.

var canvas = document.createElement('canvas');
if (canvas.getContext) { // podmínku splní jen prohlížeče podporující Canvas
  var obrazek = canvas.getContext('2d');
  obrazek.fillRect(20,20,10,15); // nakreslí černý obdélníček
  document.body.appendChild(canvas); // vloží canvas do stránky
}

Jiné tvary než obdélníky a čtverce se kreslí pomocí tzv. cest, viz příští příklad.

Nejdůležitější vlastnosti a metody dvourozměrného kontextu:

  • strokeStyle – barva čáry (implicitně black)<
  • fillStyle – barva výplně (implicitně black)
  • fillRect(x,y,w,h) – nakreslí vyplněný obdélník
  • strokeRect(x,y,w,h) – nakreslí orámovaný obdélník
  • clearRect(x,y,w,h) – smaže obdélníkovou oblast
  • fillText(text,x,y,max) – napíše text, poslední argument je maximální šířka
  • beginPath() – inicializuje (novou) cestu
  • closePath() – dokončí cestu
  • moveTo(x,y) – přerušení cesty a přemístění se na zadané souřadnice
  • lineTo(x,y) – přímá cesta na zadané souřadnice
  • rect(x,y,w,h) – obdélníková cesta
  • arc(x,y,r,úhel,úhel,směr) – cesta po obvodu kruhu (proti směru hodinových ručiček, je-li posledním argumentem true); zadané souřadnice náleží středu kruhu, úhly (první výchozí, druhý koncový) se zadávají v radiánech
  • arcTo(x1,y1,x2,y2,r) – cesta po obvodu kruhu přes první bod ke druhému
  • quadraticCurveTo(xcp, ycp, x, y) – cesta po kvadratické křivce
  • bezierCurveTo(xcp1, ycp1,xcp2, ycp2,x,y) – cesta po Bézierově křivce
  • lineWidth – tloušťka čáry (implicitně 1)
  • lineCap – zakončení čáry; butt, round, nebo square
  • isPointInPath(x,y) – zjišťuje, zda cesta prochází daným bodem
  • fill() – vyplní barvou vnitřek obrazce zformovaného cestou
  • stroke() – vykreslí cestu

Vysvětlivky: x = pozice na ose x, y = pozice na ose y, w = šířka, h = výška, r = poloměr, cp = bod, k němuž křivka uhýbá

function Vykres() {
var canvas = document.createElement('canvas');
var obrazek = canvas.getContext('2d');
obrazek.fillStyle = "green";//zelené výplně
obrazek.lineWidth = 4;// šířka čáry: 4
obrazek.beginPath(); // nová cesta
// kruh o poloměru 50
obrazek.arc(52,52,50,0,Math.PI*2,false);
obrazek.stroke(); // vykreslí černý kruh...
obrazek.fill(); // ...se zelenou výplní
obrazek.beginPath(); // nová cesta
obrazek.strokeStyle = "navy"; // další čáry budou modré
obrazek.moveTo(200,100); // přesun pozice
// formování pravoúhlého trojúhelníku
obrazek.lineTo(120,100); // první čára
obrazek.lineTo(200,20); // druhá čára
obrazek.closePath(); // dokončí trojúhelník
obrazek.stroke(); // čáry se vykreslí (modře)
document.body.appendChild(canvas);
}

A výsledek? …no hrůza!

"zelený kruh a modrý trojúhelník"

Odkazy

Klientská datová úložiště

Webové aplikace často potřebují ukládat informace o uživatelově činnosti. Tento úkol dosud plnili cookies, cookies se však již chystají do důchodu, takže nemá valný smysl o nich referovat. Nahradit by je měla datová úložiště typu sessionStorage, localStorage a database. Údaje v prvních dvou úložištích jsou uskladněny v podobě seznamu položek. Každá položka je tvořena dvojicí klíč : hodnota.

<p>
  Vítejte na našem skvělém webu! Je to zatím Vaše
  <span id="count">neznámo kolikátá </span>návštěva.
</p>
<script>
  var counter = document.getElementById('count');
  if (!localStorage.pageLoadCount)
    localStorage.pageLoadCount = 0;
  localStorage.pageLoadCount += 1;
  counter.textContent = localStorage.pageLoadCount + ". ";
</script>

Na uvedeném příkladu jsme si ukázali využití objektu localStorage. S objektem sessionStorage, který uchovává data platná po dobu aktuální relace (tj. do zavření okna nebo panelu prohlížeče), se pracuje naprosto stejně.

Třetí typ datového úložiště se svou sofistikovaností velmi liší od předešlých dvou, přestože se opět jedná o persistentní klientské datové úložiště. Má databázový charakter a slouží pro ukládání složitěji strukturovaných dat. Záznamy jsou indexovány.

Několik slov na závěr

Na internetových serverech lze narazit na mnoho tutoriálů o JavaScriptu. Některé z nich, např. w3schools.com, jsou poměrně rozsáhlé a ve srovnání s nimi bude zřejmě tato skromná syntéza působit trochu uboze, ale i ona může být užitečná, hlavně pro ty, kteří se potřebují v JavaScriptu rychle zorientovat.

Mohlo by vás také zajímat

Nejnovější

1 komentář

  1. YOYO

    Úno 25, 2010 v 5:43

    Moc děkuji za pěkné shrnutí :)

    Odpovědět

Napsat komentář

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