Objektumorientált JavaScript programozás a felszín fölött
Jelen cikkben a JavaScript objektumorientált programozásáról és szemléletéről szeretnék leírást adni. Bár már jelent meg Felhő tollából egy remek cikk a témában a Weblaboron, nem holmi véletlen folytán hasonlít a két cikk címe. De itt nem a JavaScript belső működése, hanem a programkód oldaláról szeretném megközelíteni a témát. A cikkben csak a JavaScript objektumorientált programozásával foglalkozhattam, mivel nem lehetett cél a JavaScript szintaktika tárgyalása, sem az objektumorientált programozáshoz tartozó fogalmak – objektum, osztály, metódus, öröklés stb. – magyarázata. Ennek ellenére, remélem, hogy mind a kezdők könnyűszerrel megértik a leírtakat, mind a gyakorlott programozók is hasznosnak fogják találni a cikket.
Még mielőtt elkezdenénk részletesebben foglalkozni a JavaScript objektumokkal, fontos áttekinteni a nyelv adattípusait. Azt, hogy milyen típusú változókat képes kezelni. Egyébként is, minden programozási nyelvvel való ismerkedéskor első lépésként az adott nyelv adattípusait érdemes áttekinteni. Már csak azért is, mert a változók adják a programozás alapját.
A HTML dokumentum objektum modellek (DOM) számos más adattípust definiálnak még. A HTML dokumentum és a JavaScript között az objektum modell a kapocs. Az objektum modell nem része az alap JavaScript nyelvnek, de a JavaScript léte szorosan összefügg a HTML dokumentumokkal, ezért érdemes itt megemlíteni ezeket az objektumtípusokat. A Node és az alatta szereplő adattípusok a DOM által definiált objektumtípusok.
Ahogy a fönti felsorolásból látható, a JavaScript hatféle alap adattípust ismer. Illetve van még plusz három, de számunkra most nem lényegesek, mivel azokat csak belső tárolásra használja a nyelv. A primitív adattípusok megegyeznek a más nyelvben megszokott típusokkal. Ha úgy tetszik, a JavaScriptben minden primitív típus, ami nem objektum. Sajnos néhány leírás éppen az Object adattípusról felejtkezik el, pedig épp a JavaScript objektumkezelése az igazán érdekes.
Ennek ellenére barokkos túlzás lenne azt állítani, hogy a JavaScript objektumorientált nyelv. A JavaScript egyáltalán nem követeli meg, hogy objektumorientáltan programozzunk, de azt mindenképpen mondhatjuk, hogy objektumalapú. Nem azért mert minden változó (valójában tulajdonság) objektum lenne, hanem azért mert a legfelső szinten objektumot találunk. A JavaScript primitív adattípusai nem objektumok. Ezen felül a legtöbb primitív adattípusnak létezik objektum megfelelője is, és a JavaScript – nem típusos nyelv lévén – automatikus típuskonverziót végez.
A weboldalak elemeit is objektumokon keresztül érhetjük el. A HTML dokumentumok objektum leírása, a Dokumentum Objektum Modell (DOM) azt írja le, hogy milyen szabványos objektumokkal kezelhetjük a weboldalak elemeit, és azok hogyan viselkednek. Amikor sokan szidják a JavaScript nyelv miatt ezt vagy azt a böngészőt, akkor jórészt – tudatosan vagy sem – az adott böngésző JavaScript DOM implementációjáról mondanak véleményt. Általában két eset fordul elő: vagy az, hogy a böngésző egyszerűen meg sem valósít egy objektumot vagy metódust (Opera), vagy a böngésző külön utakon jár (Internet Explorer), ami alapján nem a DOM specifikáció szerint érhetjük el a weboldalelemeket. A böngészők az alap JavaScript szintaktikában és értelmezésben megegyeznek. A Microsoft JScript és a Netscape JavaScript nyelv is hasonlóan működik, ahogy az az ECMAScript specifikációban szerepel.
Ezek után már meg tudjuk határozni, hogy hol helyezkedik el a JavaScript nyelvben azAz első sorban az e változó értéke a függvény meghívásakor aktuális esemény (event) lesz. Azért van rá szükség, mert az egér tulajdonságait, így az egér koordinátáit is az Event objektumokon keresztül érhetjük el. A második lépésben létrehozunk egy üres objektumot. Majd a következő két sorban értéket adunk az objektum x és y tulajdonságának. A JavaScript, nem típusos nyelv lévén, az objektum tulajdonságait az értékadáskor létrehozza. Majd ezzel az objektummal – ami tartalmazza az egér x és y koordinátáit – tér vissza a függvény.A
A
A JavaScript szintaktika lehetővé teszi, hogy egy tulajdonságot ne csak a pont (
A függvény első sorában egyDinamikusan hozzuk létre az ikonokat, ezért készítettünk egy
Az
Az ikonoknak ezt a hajlékony és könnyen átlátható leírását az teszi lehetővé, hogy a
A válasz nagyon egyszerű: sehogy. A JavaScript nyelv nem ismeri az osztály fogalmát. Nem tudunk osztályt létrehozni, mivel nincs ilyen.
Az első fejezetben tárgyaltuk, hogy a JavaScript nyelv célja, hogy módosíthassuk a weboldalak, illetve a böngésző elemeit, ezeket az elemeket pedig objektumokon keresztül érhetjük el. A JavaScript megalkotói szembekerülhettek azzal a problémával, hogy hogyan lehet módosítani az elemek viselkedését, miközben megtartsák az objektum alapokat. Amikor megnyitunk egy weboldalt, és a JavaScript kód fut, az aktuális weboldal elemei, mint objektumok már a JavaScript rendelkezésére állnak, vagy még be sem töltődött az oldal, ezért egyáltalán nincsenek jelen. Azt kellett feloldani, hogy egy osztály módosításakor a módosítások érvényre jussanak az osztálypéldányokon is. A JavaScript válasza a problémára az, hogy nincs osztály.
Bármelyik függvényt meghívhatjuk a Most már képesek vagyunk meghatározni az objektumok tulajdonságait és metódusait. Elő tudunk állítani bonyolultabb objektumokat is. Ennek ellenére Objektumorientált Programozásról még nem beszélhetünk, az OOP-hoz legalább két dolgot kell megvalósítani a programozási nyelvben:
Ha ez a két dolog megoldott a nyelvben, akkor beszélhetünk arról, hogy a nyelv támogatja az objektumorientált programozást. Az OOP további lehetőségei programozástechnikai dolgok, amiket vagy megvalósít egy nyelv, vagy nem. A szokásos objektumorientált nyelvekben az öröklés osztályok között történik, mivel az osztályok az objektumok sablonjai. De a JavaScript nem ismeri az osztály fogalmát, ezért ez a fajta megoldás nem jöhet szóba.
Egy user osztály számos tulajdonság és metódus definícióját tartalmazhatja. Meghatározhatjuk például azt, hogy a példányosított osztálynak lesz hajszín nevű tulajdonsága, de az osztály nem tartalmazza azt, hogy milyen színű az a haj.
A
Így a mintadarab tulajdonságai a létrehozott objektumok tulajdonságai is lesznek. AElső lépésben a konstruktor
Figyelem! A
Így kapcsolódik a
Így a prototípus láncon keresztül a
A programkódban a legfelső,
Akárhogy is, de a JavaScript csak a betöltött és felépített oldalelemekhez férhet hozzá. A szokásos OOP módon nem megoldható a feladat, hogy már kész objektumokat lehessen módosítani az osztályokon keresztül. A szokásos OOP nyelvekben sem az osztálypéldányon keresztül nem változtatható meg az osztály, sem az osztálypéldány nem fog megváltozni az osztály módosításakor.
Ezt a problémát oldja meg a prototípus szerinti objektumkezelés. Ami azt mondja, hogy nincsenek osztályok, hanem minden egy nagy objektum része. Az objektumokból alulról láthatók a fölöttük lévő elemek, az adott prototípus lánchoz tartozó mintadarabok tulajdonságai és metódusai. A prototípus lánc valósítja meg az öröklést és a tulajdonságok megosztását. Tulajdonképpen, mint láthattuk, az öröklés is a tulajdonságok megosztásán keresztül valósul meg.
Ha megváltozik egy prototípus, akkor a prototípus lánchoz kapcsolódó objektumok is a megváltozott tulajdonságokat fogják látni. Mivel azok ebben az esetben nem a saját tulajdonságukat, hanem a minta-objektum tulajdonságát használják. Ha pedig az megváltozik, akkor érthető módon az objektumlánc többi tagja is az aktuális tulajdonságértéket fogja megkapni.
Az objektumlánc legfelső helyén azA
Természetesen nem csak azA „A törpök élete nem csak játék és mese”, a csúf, gonosz Internet Explorer nem támogatja a DOM prototípusok bővítését. Már csak annál az egyszerű oknál fogva sem tehetjük ezt meg, mivel maguk a
A
Ha az objektumhoz a konstruktorban a
A
Mindezek a JavaScript prototípus szerinti objektumkezeléséből következnek. A fejezet végére érve írtam egy tesztszkriptet. A Firefox böngésző alatt mért eredmények a leírtakat hozták, igaz a két módszer között nem volt számottevő különbség. De figyelembe véve, hogy aA példakódban szereplő objektumok prototípus lánca:
Az ábra semmiben nem különbözik a prototípus lánc leírásánál szereplő ábrától. A lényeges különbség, ami nem látszik az ábrán, hogy a
Az objektumok A függvény esetében első lépésben ellenőrizzük, hogy létezhet-e a paraméter
Fontos megjegyezni, hogy ha az objektum leírásakor a
A A metódussal eldönthetjük, hogy az adott objektum, HTML elem-e vagy sem. Ha az objektum kapcsolódik a
Az objektumok
Amikor egy objektum kifejezésben szerepel, akkor implicit módon az objektum valueOf metódusa kerül meghívásra, hogy a JavaScript megkapja azt a primitív értéket, amit a kifejezés kiértékeléséhez használhat. Azon kívül, hogy az objektumainkhoz is definiálhatunk saját valueOf metódust, átírhatjuk a beépített objektumokét is. A példában felülírjuk a Érdekes dolgot érhetünk el a
Az objektumok A Objektumok, metódusok és a
A JavaScript nyelvben – és ezen már nem is csodálkozunk – a
AAzaz a pont (
Nézzünk egy másik példát, amihez hasonlókkal könnyen találkozhatunk. A weboldalakon elég gyakran előfordul, hogy egy területre kattintva egy másik terület tartalmát szeretnénk megjeleníteni vagy elrejteni. Természetesen létrehozhatunk függvényt is erre a feladatra, de sokkal hajlékonyabb megoldáshoz jutunk, ha eddigi ismereteinkre támaszkodva készítünk egy objektumot, ami összekapcsolja az egyik objektum eseményét a másik objektumon végzendő művelettel.A kód lefutása után azt hihetnénk, hogy mindent jól csináltunk, hisz ránézésre teljesen jól kell működnie ennek a nem túl bonyolult, összekötést megvalósító objektumnak. A konstruktor rendben le is fut, és beállítja a tulajdonságokat. De amint odáig merészkedünk, hogy kattintunk az egyik képen, akkor legjobb esetben is hibát fogunk visszakapni.
Az ok pedig az, hogy a
Az egyik megoldás az, hogy a konstruktorban készítünk egy burkoló függvényt, ami – a változók láthatóságának kihasználásával – megoldja, hogy aA
Természetesen a problémára egyéb megoldás is adódhat. Nem csak ilyenféle burkoló függvények használatával oldható meg ez a jelenség. Ezzel a megoldással a saját elképzelésünket erőltetjük a JavaScriptre. Egy másik megoldás lehet az, hogyha a kattintás pillanatában ismerjük a hívó elemet, akkor arra az elemre aggatjuk a többi tulajdonságot, és a kattintáskor lefutó függvény olvassa ki onnan, hogy melyik elemet kell megváltoztatni.
Tanulmányozáshoz itt egy bővebb változata az objektumnak, ami a megadott sütibe elmenti az állapotot, és az objektumok létrehozásakor visszaállítja a felhasználó számára az oldal legutóbbi kinézetét.
A statikus változóra a választ az objektumok megismerése adja. Tudjuk azt, hogy maguk a függvények is változók,
Mire használhatunk statikus változókat? Például eredmények mentésére, azaz gyorsítótárazásra, ami a JavaScript esetén nem jön rosszul. A következő metódus erre mutat egy példát:A
A JSON leírás azt mondja, hogy az elküldendő adatokat írjuk le a JavaScriptben használatos objektum vagy tömb literállal. Majd ezt a literált küldjük el szövegként, amit majd a fogadó értelmezni fog. Annyi megszorítást tesz, hogy objektumok esetén a név-érték pároknál a nevet kettős idézőjelek (") közzé kell tenni, az értékek pedig a következők lehetnek: kettős idézőjelekkel határolt sztring, szám,A cikk elején számos ilyen objektum literállal ismerkedhettünk meg. A JSON leírás azt mondja, hogy küldjük el az ilyen adathalmazt szövegként. A veszélyt éppen ez jelentheti, mivel azért, hogy megkapjuk a JavaScript jelölésrendszer szerint összeállított adatokat, a szöveget általában valamiféle A metódus első lépésben kiszedi a kapott szövegből a kettős idézőjelek (") között lévő szövegrészeket, majd a megmaradt karaktereket ellenőrzi. Ezek a karakterek a sztringen kívül engedélyezett értékekből adódnak:
A cikkben szereplő kódokat letöltheted, ha nem szeretnél foglalkozni a gépelésükkel.
■ A JavaScript nyelv
Minden programozási nyelvet valamiféle céllal hozták létre, a JavaScript sem általános célú programozási nyelv. Azért alkották, hogy a weboldalakat és a böngészőt dinamikusan lehessen változtatni. A kliens oldali JavaScript nincs böngésző nélkül. Sőt, az a feladata, hogy a nyelvet kiszolgáló környezetet lehessen módosítani.Még mielőtt elkezdenénk részletesebben foglalkozni a JavaScript objektumokkal, fontos áttekinteni a nyelv adattípusait. Azt, hogy milyen típusú változókat képes kezelni. Egyébként is, minden programozási nyelvvel való ismerkedéskor első lépésként az adott nyelv adattípusait érdemes áttekinteni. Már csak azért is, mert a változók adják a programozás alapját.
A JavaScript adattípusai
- Number: Primitív adattípus. Mind pozitív, mind negatív, egész és tört, szóval mindenféle szám tárolásához. A Number adattípus értéktartományába tartozik még az Infinity (végtelen), a -Infinity (negatív végtelen) és a NaN (nem-szám) érték is.
- String: Primitív adattípus szövegek tárolásához.
- Boolean: Primitív adattípus logikai értékek, true vagy false tárolásához.
- Null: Primitív adattípus a null érték tárolásához. A null elnevezés adattípust és értéket is jelent. A Null olyan adattípus, aminek egyetlen egy értéke lehet, a null.
- Undefined: Primitív adattípus a definiálatlan érték jelzésére. Az Undefined adattípus értéktartományába egy elem, az undefined tartozik.
- Object: Objektum adattípus. Az általunk definiált objektumok ebbe a csoportba tartoznak. Az alap JavaScript nyelv a következő kilenc beépített objektumtípust tartalmazza még:
- Number: A primitív adattípusnak megfelelő Number objektum.
- String: A primitív adattípusnak megfelelő String objektum.
- Boolean: A primitív adattípusnak megfelelő Boolean objektum.
- Array: Tömb létrehozására használható adattípus. A JavaScript nyelv csak egydimenziós tömböt ismer, de a tömbelemek szintén lehetnek Array objektumok, így létrehozható többdimenziós tömb. Egyébként a tömbelemek bármilyen típusúak lehetnek.
- Math: Matematikai objektum különböző matematikai állandók és függvények eléréséhez. A Math objektumnak nincs konstruktora, így nem hozható létre belőle több példány.
- Date: Dátum adattípus dátum kezeléséhez, és különböző dátumokkal kapcsolatos állandók és függvények eléréséhez.
- RegExp: Mintaillesztő kifejezés létrehozásához használatos adattípus.
- Error: Különféle hibakeresést segítő objektumok.
- Function: Függvények létrehozásához használatos adattípus. A függvénynév a változónévnek felel meg, és ugyanúgy viselkedik, mint bármelyik más változó.
- Node
- Document
- HTMLDocument
- Element
- HTMLElement
- HTMLHeadElement
- HTMLBodyElement
- HTMLInputElement
- …
- HTMLHeadElement
- HTMLElement
- …
- Document
- Number: A primitív adattípusnak megfelelő Number objektum.
A HTML dokumentum objektum modellek (DOM) számos más adattípust definiálnak még. A HTML dokumentum és a JavaScript között az objektum modell a kapocs. Az objektum modell nem része az alap JavaScript nyelvnek, de a JavaScript léte szorosan összefügg a HTML dokumentumokkal, ezért érdemes itt megemlíteni ezeket az objektumtípusokat. A Node és az alatta szereplő adattípusok a DOM által definiált objektumtípusok.
Ahogy a fönti felsorolásból látható, a JavaScript hatféle alap adattípust ismer. Illetve van még plusz három, de számunkra most nem lényegesek, mivel azokat csak belső tárolásra használja a nyelv. A primitív adattípusok megegyeznek a más nyelvben megszokott típusokkal. Ha úgy tetszik, a JavaScriptben minden primitív típus, ami nem objektum. Sajnos néhány leírás éppen az Object adattípusról felejtkezik el, pedig épp a JavaScript objektumkezelése az igazán érdekes.
Ennek ellenére barokkos túlzás lenne azt állítani, hogy a JavaScript objektumorientált nyelv. A JavaScript egyáltalán nem követeli meg, hogy objektumorientáltan programozzunk, de azt mindenképpen mondhatjuk, hogy objektumalapú. Nem azért mert minden változó (valójában tulajdonság) objektum lenne, hanem azért mert a legfelső szinten objektumot találunk. A JavaScript primitív adattípusai nem objektumok. Ezen felül a legtöbb primitív adattípusnak létezik objektum megfelelője is, és a JavaScript – nem típusos nyelv lévén – automatikus típuskonverziót végez.
A weboldalak elemeit is objektumokon keresztül érhetjük el. A HTML dokumentumok objektum leírása, a Dokumentum Objektum Modell (DOM) azt írja le, hogy milyen szabványos objektumokkal kezelhetjük a weboldalak elemeit, és azok hogyan viselkednek. Amikor sokan szidják a JavaScript nyelv miatt ezt vagy azt a böngészőt, akkor jórészt – tudatosan vagy sem – az adott böngésző JavaScript DOM implementációjáról mondanak véleményt. Általában két eset fordul elő: vagy az, hogy a böngésző egyszerűen meg sem valósít egy objektumot vagy metódust (Opera), vagy a böngésző külön utakon jár (Internet Explorer), ami alapján nem a DOM specifikáció szerint érhetjük el a weboldalelemeket. A böngészők az alap JavaScript szintaktikában és értelmezésben megegyeznek. A Microsoft JScript és a Netscape JavaScript nyelv is hasonlóan működik, ahogy az az ECMAScript specifikációban szerepel.
Ezek után már meg tudjuk határozni, hogy hol helyezkedik el a JavaScript nyelvben az
Object
objektumtípus. Hozzákezdhetünk közelebbi barátságot kötni ezzel az adattípussal.Objektum a kimeneten
Minden bizonnyal előfordult már, hogy szerettük volna elérni, hogy egy függvény egyszerre több értékkel térjen vissza. Általában akkor fordul elő ilyen helyzet, amikor olyan adatokat várunk, amik szorosan egymáshoz tartoznak. A JavaScriptben használhatjuk erre az objektumokat, amik név-érték párok csoportjai.Az egér pozíciójának lekérdezése
Az egyik ilyen függvény, ahol szükségét láthatjuk annak, hogy objektummal térjen vissza, az egérpozíció lekérdezését végző függvény lehet. Ahol egyszerre kell visszaadnunk mindkét koordinátát.function getMouse(e){
e = e || window.event;
//Egy új objektumot hozunk létre.
var mouse = new Object();
//A mouse nevű objektum x és y tulajdonságának definiálása
mouse.x = (e.pageX || e.clientX);
mouse.y = (e.pageY || e.clientY);
//Visszatérünk az objektummal
return mouse;
}
//A getMouse függvény használatának bemutatása
document.onclick = test;
function test(e){
var m = getMouse(e);
alert( "Az egér pozíciója: " + m.x + "×" + m.y );
}
Az objektum literál
Látható, hogy egyáltalán nem bonyolult új objektumot létrehozni, és használatukkal nem csak a függvény működése átláthatóbb, de a visszakapott értékkel is könnyebb dolgozni. Szerencsére megfogtuk az Isten lábát, mert a JavaScript alkotói – igazodva ahhoz, hogy a JavaScript egy szkript nyelv – számos esetben többféle szintaktikát is lehetővé tettek. Létrehozhatunk objektumokat objektum literállal is, ami az előbbinél olvashatóbb formátumot eredményez. Objektumra a kapcsos zárójelekkel ({}
) hivatkozhatunk.function getMouse(e){
e = e || window.event;
//Ez a rész ugyan azt eredményezi, mint az előző függvény programsorai.
return {
x : (e.pageX || e.clientX),
y : (e.pageY || e.clientY)
}
}
//A getMouse függvény használatának bemutatása
document.onclick = test;
function test(e){
var m = getMouse(e);
alert( "Az egér pozíciója: " + m.x + "×" + m.y );
}
return
után létrehozunk egy objektumot, amiben fölsoroljuk az objektumhoz tartozó név-érték párokat. Az objektum literálban az értékadáshoz az egyenlőségjel helyett a kettőspont használatos, a név-érték párokat pedig vesszővel kell elválasztanunk. Így egy lépésben létrehozzuk az objektumot, és értéket adunk az x és y tulajdonságoknak is. A létrehozott objektumot tárolhatnánk egy változóban is, ahogy az első példában tettük. De jelenleg a függvényben semmit nem kezdünk vele, ezért a return rögtön visszatérhet az előállított eredménnyel.Literál
A programban szereplő nem nevesített konstanst jelenti. A fönti kódrészletben sztring literál az "Az egér pozíciója: " szöveg. Egyébként betű szerinti alakot jelent. A JavaScript objektum literálja nem minden esetben igazi literál, a tulajdonságértékek meghatározásakor a kifejezések futásidőben értékelődnek ki.Objektum a bemeneten
AgetMouse
függvény hívásakor egy könnyen kezelhető visszatérési értéket kaptunk. Ugyanígy használhatunk objektumot függvényparaméterként is. Számos esetben előfordul, hogy nem tudjuk előre a függvény paramétereinek számát, változó paraméterlistára lenne szükség. Erre az egyik megoldás, hogy a függvény hívásakor automatikusan létrejövő arguments
változót használjuk a függvény paramétereinek kinyeréséhez. A másik megoldás lehet, hogy objektumot alkalmazunk paraméterként.HTML elemek tulajdonságainak megváltoztatása
A weboldalak módosítása során elég gyakran előfordul, hogy meg kell változtatnunk a weboldal egy elemének a tulajdonságait. Mivel gyakran ismétlődő műveletről van szó, érdemes fölemelni egy magasabb absztrakciós szintre. Készítsünk hozzá egy függvényt, ami paraméterül egy weboldal elemet és a megváltoztatandó tulajdonságokat kapja.function setAttribs(element, attribs){
//Végiglépkedünk az attribs objektum elemein
for (var i in attribs){
if (typeof attribs == "object"){
//Az elem szintén objektum, meghívjuk erre is a függvényt
setAttribs(element[i], attribs[i]);
}else {
//Az elem adott tulajdonságának megváltoztatása.
try{ element[i] = attribs[i]; } catch(err){}
}
}
}
//A setAttribs függvény használatának bemutatása
var testElement = document.getElementById("header_element");
setAttribs(testElement,
{
title: "Ez egy felirat",
style:
{
cursor: "help",
"text-decoration": "underline"
}
}
);
setAttribs
függvény hívását több sorba törtem, hogy a második paraméter felépítése jobban látható legyen. A függvény első paramétere rendszerint a weboldal egy eleme, az attribs
paraméter pedig egy objektum, ami az első paraméter beállítani kívánt tulajdonságait tartalmazza. A példában értéket adunk a megadott HTML elem title
tulajdonságának, és megváltoztatjuk a stílusát.A JavaScript szintaktika lehetővé teszi, hogy egy tulajdonságot ne csak a pont (
.
) operátorral érjük el, hanem hivatkozhatunk rá kapcsos zárójelekkel ([]
) is. Ebben az esetben a tulajdonságnevet megadhatjuk sztringgel és változóval is. Ha nem ismerjük a tulajdonság nevét – ahogy a setAttribs
függvény esetében – akkor ez utóbbi szintaktikát kell alkalmaznunk.A függvény első sorában egy
for in
ciklust indítunk, végiglépkedünk az attribs
objektum elemein. Ha az attribs
aktuális eleme objektum, akkor rekurzívan meghívjuk arra az objektumra a setAttribs
függvényt. Ha az érték nem objektum, akkor az element
adott tulajdonságára megpróbáljuk beállítani az értéket. A paraméterként átadott attribs
objektum név-érték pároknak meg kell felelni az element
objektum tulajdonságnév-érték párjainak, ahogy ez a függvény hívásánál látható.A Weblabor ikonsorának beállítása
A Weblaboron az ikonsor diszkréten jön létre. Csak akkor jelennek meg a plusz ikonok, ha be van kapcsolva a JavaScript. AsetAttribs
függvényt használva átláthatóbb és könnyebben módosítható eszköztárat létrehozó függvényt alkothatunk.function addIcons(){
//Az oldalon az ikonokat a toolset azonosítójú div elem tartalmazza.
var area = document.getElementById("toolset");
if (!area) { return; }
//Az ikonok felsorolását egy tömb tartalmazza.
//A tömb elemei pedig objektumok, amik az ikonokat írják le.
var icons = [
{
src : "/themes/azigazi/print.gif",
alt : "Nyomtatás",
title : "A dokumentum nyomtatása",
id : "tool-print",
style : {
cursor:"pointer",
"font-size":"25%"
},
onclick: printPage
},
{
src : "/themes/azigazi/bigfont.png",
alt : "Nagyobb betűméret",
title : "Nagyobb betűméretre váltás",
id : "button-bigfont",
style : {cursor:"pointer", "font-size":"25%"},
onclick: function(){ setStyle("style-bigfont"); }
},
{
src : "/themes/azigazi/smallfont.png",
alt : "Normál betűméret",
title : "Normál betűméretre váltás",
id : "button-smallfont",
style : {cursor:"pointer", "font-size":"25%"},
onclick: function(){ setStyle("style-smallfont"); }
}
];
//Végiglépkedünk a tömb elemein, létrehozunk egy img elemet, és hozzáadjuk az ikonsorhoz.
for (var i in icons){
area.appendChild( createElement("img", icons[i]) );
}
}
function createElement(type, attribs){
//Adott típusú elem létrehozása, majd beállítjuk a megadott tulajdonságokat.
var e = document.createElement(type);
setAttribs(e, attribs);
return e;
}
createElement
függvényt. Ez azzal több a document.createElement
metódusnál, hogy az elemhez beállítja a megadott tulajdonságokat is.Az
addIcons
függvény pedig bár hosszúnak tűnik, de valójában három sorból áll. Az ikonok leírását az icons
tömb tartalmazza, a tömb elemei objektumok, amik a képek leírásait tartalmazzák. A setStyle
és a printPage
függvények szerepelnek a Weblabor JavaScript fájljában. De a paraméterátadás miatt a setStyle
hívásához nem nevesített burkoló függvényeket hozunk létre.Az ikonoknak ezt a hajlékony és könnyen átlátható leírását az teszi lehetővé, hogy a
setAttribs
függvény paraméteréül, a tulajdonságok meghatározásához objektumot használtunk.Hogyan hozzunk létre osztályt?
Most már egészen jól tudjuk alkalmazni a JavaScript objektumokat visszatérési értékként és paraméterként is. Miután nem elégedünk meg az objektumok közvetlen létrehozásával, fölmerül a kérdés: hogyan kell osztályt létrehozni?A válasz nagyon egyszerű: sehogy. A JavaScript nyelv nem ismeri az osztály fogalmát. Nem tudunk osztályt létrehozni, mivel nincs ilyen.
Az első fejezetben tárgyaltuk, hogy a JavaScript nyelv célja, hogy módosíthassuk a weboldalak, illetve a böngésző elemeit, ezeket az elemeket pedig objektumokon keresztül érhetjük el. A JavaScript megalkotói szembekerülhettek azzal a problémával, hogy hogyan lehet módosítani az elemek viselkedését, miközben megtartsák az objektum alapokat. Amikor megnyitunk egy weboldalt, és a JavaScript kód fut, az aktuális weboldal elemei, mint objektumok már a JavaScript rendelkezésére állnak, vagy még be sem töltődött az oldal, ezért egyáltalán nincsenek jelen. Azt kellett feloldani, hogy egy osztály módosításakor a módosítások érvényre jussanak az osztálypéldányokon is. A JavaScript válasza a problémára az, hogy nincs osztály.
Objektumorientált programozási szemléletek
Valahol olvastam a következő mondatot:Aki látott egy asztalt, az látta valamennyit.
Platón ideák tana és az objektumorientált programozás
A mondat egyikféle értelmezése a platóni filozófiából, az ideák tanából ered. Gondolati világunkban minden létezik, aminek végső eredője a szellemi világ. Amikor valaki kitalál, felfedez, vagy megalkot valamit, tulajdonképpen visszaemlékszik az isteni ideára. A gondolati világban létezik, létezett és létezni fog „AZ Asztal”. Ellentétben a fizikai világ asztalaival. Az objektumorientált programozásban egy asztal osztály „AZ Asztal” ideának a leírása.A fizikai szemlélet
A másik, Platón szemléletétől eltérő megközelítés a fizikai szemlélet. Ha már láttál egy asztalt – mivel mindegyik valamiképpen hasonlít egymásra – a többit is fölismered. Ezért bármelyik asztal megismerése után felismerjük a többit is, ha a szemünk elé kerül. A feltétele a felismerésnek, hogy megismerkedjünk a fizikai világunkban az adott dologgal. A JavaScript objektumorientált szemlélete – hogy elhagyhassa az osztály fogalmát – ez utóbbi megközelítést követi. A JavaScript azt mondja, hogy:Mutass egy objektumot, ilyen a többi is. Ha mégsem ilyen, akkor majd mondd meg, hogy miben különbözik!
Objektumok gyártása
Ha nem közvetlenül, objektum literállal – ahogy azt a bevezető fejezetekben tettük – akarjuk létrehozni az objektumokat, akkor az objektum előállításához anew
operátort kell használnunk. Bármelyik függvény végrehajtásával előállíthatunk objektumot, ha a függvényt a new
operátorral hívjuk meg. A függvény ekkor az objektum konstruktora (készítője, előállítója) lesz.A konstruktor, az objektum előállítója
A következő sorok üres objektumokat állítanak elő, amiből csupán a konstruktorhívás, az objektumlétrehozás szintaktikája látható.//Objektum előállítása objektum literállal.
var obj1 = {};
//Objektum előállítása a beépített Object konstruktorral.
var obj2 = new Object();
//Objektum előállítása saját konstruktorral.
function constructorEmptyObject(){ }
var obj = new constructorEmptyObject();
//Egy sorba tömörítve.
var obj = new function constructorEmptyObject(){};
new
operátorral, akár a setAttribs
függvényt is. De a setAttribs
függvény nem konstruktornak készült, ezért konstruktorként meghívva – ahogy a példában szereplő constructorEmptyObject
esetében is – csak üres objektumot fogunk visszakapni. Ha egy függvényt, amit konstruktornak készítettünk, a new
operator nélkül hívunk meg, az eredmény a konstruktortól fog függeni, és a konstruktor mint függvény fog végrehajtódni. Meghívhatjuk a beépített String
konstruktort is a new
operátor nélkül. A String("Egy sztring")
kifejezés egy primitív sztringet fog visszaadni, nem objektumot.//String objektum előállítása.
var objStr = new String("Egy sztring");
//Primitív String típust ad vissza.
var prmStr = String("Egy sztirng");
//Object típusú objektumot állít elő.
var objObj = new constructorEmptyObject();
//A változó értéke undefined lesz, mivel nem határoztuk meg a visszatérési értéket.
var unDef = constructorEmptyObject();
A tulajdonságok meghatározása
Erősen korlátozott az üres objektumok használata, amik semmilyen tulajdonsággal nem rendelkeznek. A konstruktorokban megadhatjuk az előállított objektum tulajdonságait. A konstruktorként használt függvényben az előállított objektumra athis
([i]ez) szóval tudunk hivatkozni.function constructorObject(value){
//A konstruktorban a this szóval az éppen előállított objektumra hivatkozunk.
this.foo = 2;
this.bar = value;
}
var obj = new constructorObject(5);
alert( obj.foo + obj.bar );
A metódusok definiálása
A cikk elején, az adattípusok leírásánál láthattuk, hogy a függvények is egyfajta változók,Function
típusú objektumok. A konstruktorban ugyanúgy definiálhatunk metódusokat, ahogy tulajdonságokat. Illetve minden olyan objektumtulajdonság metódus lesz, aminek a típusa Function
.function constructorObject(value){
//Egy foo nevű tulajdonság meghatározása a konstruktorban.
this.foo = value;
//Egy addFoo metódus definiálása a konstruktorban.
this.addFoo = function(plus){
this.foo += plus;
}
}
- Objektumok létrehozása tulajdonságokkal és metódusokkal
- Öröklés megvalósítása
Ha ez a két dolog megoldott a nyelvben, akkor beszélhetünk arról, hogy a nyelv támogatja az objektumorientált programozást. Az OOP további lehetőségei programozástechnikai dolgok, amiket vagy megvalósít egy nyelv, vagy nem. A szokásos objektumorientált nyelvekben az öröklés osztályok között történik, mivel az osztályok az objektumok sablonjai. De a JavaScript nem ismeri az osztály fogalmát, ezért ez a fajta megoldás nem jöhet szóba.
A prototípus szerinti objektumkezelés
Minden függvény rendelkezik egyprototype
(mintadarab, prototípus) nevű tulajdonsággal, ez a tulajdonság vagy Object
vagy Null
típusú értéket tartalmaz. A prototype
akkor kap jelentőséget, ha a függvényt a new
operátorral hívjuk meg, azaz amikor a függvény konstruktorként működik. A prototype
tartalmazza az objektum előállításához használatos mintadarabot, aminek megadásával azt mondjuk a JavaScript számára, hogyÍme egy objektum, így néz ki a többi is. Ha mégsem ilyen lenne, majd megmondom, miben különbözik.
//Konstruktor Person típusú objektumok létrehozásához.
function Person(familyName, foreName){
//A megadott név tárolása
this.name = [familyName, foreName];
//Egyéb tulajdonságok beállítása
this.count.index += 1;
this.index = this.count.index;
}
//A mintadarab megadása a konstruktor prototype tulajdonságával.
Person.prototype = {
//A tulajdonságok bármilyen típusúak lehetnek.
name : null,
status : 1,
//Néhány további tulajdonság megadása.
index : 0,
count : { index : 0 },
constructor : Person,
//Az objektum metódusai.
setName : function(familyName, foreName){
//A metódusokban vigyázva a this szó használatával! Erről majd később.
this.name = [familyName, foreName];
},
getName : function(){
//A teljes név visszaadása, igaz az objektumok
//tulajdonságai publikusak, azaz közvetlenül is elérhetők.
return this.name[0] + " " + this.name[1];
}
};
//Két Person típusú objektum létrehozása.
var person1 = new Person("Csavardi", "Samu");
var person2 = new Person("Zsákos", "Frodó");
//Példák a tulajdonságok elérésére és metódusok hívására
alert( person1.index + " : " + person1.getName() + " : " + person1.status );
alert( person2.index + " : " + person2.getName() + " : " + person2.status );
A mintadarab nem osztály
Fontos, hogy sem a konstruktor, sem a konstruktorprototype
tulajdonsága nem azonos a megszokott osztály fogalmával. Bár, ha könnyebb számunkra, akár gondolhatunk rá így is. Az osztály minden esetben sablon. Egy általános leírás arról, hogy az adott objektum hogyan működik, és milyen tulajdonságai vannak. Konkrét értékeket nem tartalmaz. Ha mégis, az magára az osztályra vonatkozik, és nem az objektumra, vagy a példányosítás megkönnyítése miatt tartalmazza egyes tulajdonságok alapértelmezett értékeit.Egy user osztály számos tulajdonság és metódus definícióját tartalmazhatja. Meghatározhatjuk például azt, hogy a példányosított osztálynak lesz hajszín nevű tulajdonsága, de az osztály nem tartalmazza azt, hogy milyen színű az a haj.
A
prototype
a nevének megfelelően egy mintadarabot tartalmaz, tulajdonságokkal, metódusokkal és értékekkel együtt.A prototípus lánc
Egy objektum létrehozásakor, még a konstruktor futása előtt létrejön egy belső hivatkozás az éppen előállított objektum és aprototype
tulajdonságban tárolt objektum, azaz a mintadarab között. A fenti példánál maradva a status
tulajdonságot csak a prototype
-nál definiáltuk. Amikor a kódban hivatkozunk az person1.status
tulajdonságra, akkor a JavaScript előbb megnézi, hogy maga a person1
objektum rendelkezik-e ilyen tulajdonsággal. Ha nem, akkor megvizsgálja, hogy az objektumhoz rendelt prototype
rendelkezik-e vele, és mivel a prototype
is egy objektum, és ahhoz az objektumhoz is tartozik egy belső prototype
hivatkozás, ami vagy szintén objektum vagy null
érték, a JavaScript tovább folytathatja a sort, amíg a végére (elejére) nem ér. Ha ebben a láncban megtalálja a keresett tulajdonságot, akkor visszatér az értékével.Így a mintadarab tulajdonságai a létrehozott objektumok tulajdonságai is lesznek. A
prototype
– illetve az objektumhoz tartozó prototípus lánc – felelős az adott objektumok között a tulajdonságok megosztásáért. A person1
és a person2
objektumok status
tulajdonsága ugyanazon értéket adják vissza, mert a konstruktoruk prototype.status
tulajdonságát jelentik.A prototípus szerinti öröklés
Ez a belső prototípus lánc teszi lehetővé – a JavaScript ezen keresztül valósítja meg – az öröklést. Ha tovább szeretnénk örökíteni az adott objektumot, vagy még inkább, ha tovább szeretnénk fűzni a prototípus láncot, akkor a konstruktorprototype
tulajdonságának az örökíteni kívánt objektumot kell értékül adnunk. Így a konstruktor prototype
tulajdonsága egy belső hivatkozást fog tartalmazni az örökített objektum – normál objektumorientált nyelvben ez a szülő osztály – mintadarabjára.
//A kiterjesztett objektumok konstruktora.
//Minden olyan beállítást, amit az örökölt objektum konstruktorában kellene megadnunk,
//itt is meg kell adnunk, vagy közvetlenül nekünk kell meghívnunk az előző konstruktort.
function VIP(familyName, foreName){
this._proto_.constructor.call(this, familyName, foreName);
this.status = 1;
}
//A prototípus lánc továbbfűzése.
VIP.prototype = new Person();
//A mintadarab további tulajdonságainak megadása.
//Itt nem használhatunk objektum literált, különben felülírnánk az előző objektumot.
VIP.prototype.getStatus = function(){
return this.status;
}
//Megadunk egy hivatkozás az előző prototípusra.
VIP.prototype._proto_ = Person.prototype;
//Egy VIP típusú objektum előállítása.
var person3 = new VIP("Szürke", "Gandalf");
alert( person3.index + " : " + person3.getName() + " : " + person3.status );
VIP.prototype
tulajdonsága egy Person
objektum lesz. Így kapcsolódik a VIP.prototype
objektum a Person
prototípus láncához. A VIP.prototype
objektumot a továbbiakban bővíteni nem tudjuk objektum literállal, különben fölülírnánk a new Person()
kifejezés által meghatározott értéket. A prototípus további tulajdonságainak – például azoknak a tulajdonságoknak, amiket pluszban hozzá szeretnénk adni – a pont (.) operátorral tudunk értéket adni.Figyelem! A
Person
konstruktor csak egyszer hajtódik végre. Akkor, amikor – egy objektum létrehozásával – kapcsolódtunk az adott prototípus lánchoz. De a továbbiakban, a VIP
típusú objektumok létrehozásakor a Person
konstruktor nem fog végrehajtódni. Az objektumok létrehozásakor csak egy konstruktor fut le. Ezért, ha szükséges az előző prototípus konstruktorát nekünk kell közvetlenül meghívnunk.Így kapcsolódik a
VIP.prototype
objektum a Person
prototípus láncához, és egyben saját további tulajdonságokkal is rendelkezhet, amiket az alatta lévő objektumokkal oszt meg. Ennél a példánál maradva, a prototípus lánc így néz ki:Object.prototype
|
- Person.prototype
|
- person1
|
- person2
|
- VIP.prototype
|
- person3
|
- Person.prototype
|
- person1
|
- person2
|
- VIP.prototype
|
- person3
Így a prototípus láncon keresztül a
person3
objektum hozzáfér mind a VIP
konstruktor, mind a Person
konstruktor, mind az Object
konstruktor prototype objektumának tulajdonságaihoz. Ezek a tulajdonságok mind úgy fognak viselkednek, mint a person3 saját tulajdonságai. Ezen kívül a person3 nem fér hozzá az person1 vagy person2 tulajdonságaihoz, vagy egyéb objektumokhoz, és a person1 objektum sem fér hozzá a VIP konstruktor prototype
objektumához, sem a person3
objektumhoz, mivel ezek vagy ugyanazon a szinten vannak, vagy alatta helyezkednek el a prototípus láncban.A programkódban a legfelső,
Global
típusú objektumtól indulva explicit módon bármelyik objektum – nem Internal
– tulajdonsága elérhető. A böngészőkben ez a window
objektum. Természetesen az előbb implicit hivatkozásokról volt szó. A hivatkozások feloldásával nem nekünk kell foglalkoznunk. Az objektumok által látott tulajdonságok az objektumok saját tulajdonságaként viselkednek.Amiért elfelejtkezett a JavaScript az osztályról
A JavaScriptet eredetileg a weboldalak és a böngészők dinamikusabbá tételéért hozták létre. Ami azt jelenti, hogy ellentétben a többi nyelvvel, a JavaScriptnek már egy kész rendszerben kell tevékenykednie, és az őt kiszolgáló környezet elemeit módosítania. A JavaScript futásakor a weboldal már felépült, vagy egyáltalán nem is létezik még a JavaScript részére.Akárhogy is, de a JavaScript csak a betöltött és felépített oldalelemekhez férhet hozzá. A szokásos OOP módon nem megoldható a feladat, hogy már kész objektumokat lehessen módosítani az osztályokon keresztül. A szokásos OOP nyelvekben sem az osztálypéldányon keresztül nem változtatható meg az osztály, sem az osztálypéldány nem fog megváltozni az osztály módosításakor.
Ezt a problémát oldja meg a prototípus szerinti objektumkezelés. Ami azt mondja, hogy nincsenek osztályok, hanem minden egy nagy objektum része. Az objektumokból alulról láthatók a fölöttük lévő elemek, az adott prototípus lánchoz tartozó mintadarabok tulajdonságai és metódusai. A prototípus lánc valósítja meg az öröklést és a tulajdonságok megosztását. Tulajdonképpen, mint láthattuk, az öröklés is a tulajdonságok megosztásán keresztül valósul meg.
Ha megváltozik egy prototípus, akkor a prototípus lánchoz kapcsolódó objektumok is a megváltozott tulajdonságokat fogják látni. Mivel azok ebben az esetben nem a saját tulajdonságukat, hanem a minta-objektum tulajdonságát használják. Ha pedig az megváltozik, akkor érthető módon az objektumlánc többi tagja is az aktuális tulajdonságértéket fogja megkapni.
Az objektumlánc legfelső helyén az
Object.prototype
tulajdonság által leírt objektumot találjuk. Ezért ezen objektum prototype
tulajdonságának módosítása minden JavaScript objektumra hatással van. A módosítás mind a prototype
tulajdonság módosítása után, mind a módosítása előtt létrehozott objektumokra ki fog hatni.//Hivatkozás a HTML oldal html elemére.
var html = document.getElementsByTagName("html")[0];
//A tulajdonság értéke undefined, mert nem definiáltuk.
alert( html.MAX_VALUE );
//Az Object prototype bővítése.
Object.prototype.MAX_VALUE = 1;
//A tulajdonság már nem undefined, mert a prototípus láncában már fellelhető a tulajdonság.
alert( html.MAX_VALUE );
//Ennek nem 1 az értéke.
alert( Number.MAX_VALUE );
Number
objektumhoz – a Number
típus konstruktorához – alapértelmezetten definiálva van a MAX_VALUE
tulajdonság. Ezért amikor arra hivatkozunk, a JavaScript nem keresi tovább a tulajdonságot a prototípus láncban, mivel közvetlenül megtalálta az objektumnál. A Number
objektumhoz meghatározott MAX_VALUE
elfedi a prototípus láncban szereplő értéket.Természetesen nem csak az
Object.prototype
mintadarabot módosíthatjuk, bármelyik JavaScript objektum prototípusát megváltoztathatjuk, amivel elérhetjük, hogy a többi ugyanolyan típusú objektum is a megváltozott tulajdonságot fogja látni.Alap JavaScript objektumtípusok bővítése
Különféle tulajdonságokkal és újabb metódusokkal bővíthetjük a beépített objektumtípusokat is. A példában aDate
típusú objektumokat egy hasznosnak vélt metódussal egészítjük ki.Date.prototype.getJD = function(){
//Az aktuális értékek kinyerése, a hónapok sorszámozása 0-val kezdődik.
var y = this.getFullYear(),
m = this.getMonth()+1,
d = this.getDate(),
jd = 367*y-7*(y+(m+9)/12)/4+(275*m/9)+d+1721027;
if (jd > 2299170) jd += 2-3*((y+(m-9)/7)/100+1)/4; /*2299170=>1582.10.04.*/
return parseInt(jd);
}
getJD
metódus az adott dátumot konvertálja át Julian date értékre. A Julian date-nek semmi köze a Julianus naptárhoz, annál inkább Joseph Justus Scaliger csillagászhoz. A Julian date a Kr.e. 4712.01.01.-től eltelt napok számát adja meg, figyelembe véve a Gregorian naptárra való átállást is. Az értéket a csillagászatban periodikus események vizsgálatához használják. Hasonlatos a Unix időbélyeghez, csak a Julian date nullpontja a Kr.e. 4712.01.01. dátum, és a mértékegység nem másodperc, hanem nap. Ha másodperc-időbélyeg helyett nap-időbélyegre van szükségünk, inkább használjuk a Julian date értéket.//A prototípus kiegészítése után a dátum típusú változóknál használhatjuk a metódust.
var d = new Date;
alert( "Kr.e.4712.01.01. óta eltelt napok száma: " + d.getJD() );
HTML elemtípusok bővítése
Már meg tudjuk változtatni a JavaScript beépített objektumtípusait, de a JavaScript oldaláról nézve a weboldal elemek is ugyanolyan objektumok, mint az alap objektumok. Azaz ugyanúgy módosíthatóak, és a prototípus lánc segítségével a módosítások érvényre jutnak.//A táblázatcellák prototípusához megadunk egy Message tulajdonságot.
HTMLTableCellElement.prototype.Message = "%1. táblázatcella";
window.onload = function(){
//A táblázatok minden cellájához megjelenítjük a prototípusnál megadott szöveget.
var c = document.getElementsByTagName("td");
for (var i in c){
c.onclick = function(){
alert( this.Message.replace(/%1/, this.cellIndex+1) );
}
}
}
Node
, Document
, Element
, HTMLElement
stb. objektumok sem találhatóak meg a window
objektumban, így aztán nehéz is ezen objektumok prototype
tulajdonságát megváltoztatni.Melyiket szeressem?
Prototype vagy konstruktor
Eddig eljutva jó rálátásunk lehet a JavaScript objektumkezelésére, és minden bizonnyal feltűnt, hogy kétféle módon határozhatjuk meg az előállítandó objektum tulajdonságait.- A prototype tulajdonságon keresztül
- A konstruktorban a
this
kulcsszóval
A
prototype
alkalmazásakor az objektum tulajdonságára való hivatkozáskor – ahogy azt az előbb megismertük – a JavaScript megvizsgálja az objektumot, hogy megtalálható-e az adott tulajdonság, mivel ott nem találja meg, folytatja a keresést a prototípus láncban.Ha az objektumhoz a konstruktorban a
this
kulcsszóval adjuk hozzá a tulajdonságot, és amikor hivatkozunk rá, a JavaScript az objektumnál megtalálja, nem keresgél tovább a prototípus láncban. Ellenben ezt a módszert alkalmazva nem tudjuk egy lépésben – a prototype
módosításával – megváltoztatni a tulajdonságot, mert a létrehozott objektumok elfedik azt.A
prototype
használatával memóriát takaríthatunk meg, és az objektumok is gyorsabban hozhatók létre, de a tulajdonságok elérése – ha nem is lényegesen, de – lassabb lehet. Ha a tulajdonságokat a konstruktorban definiáljuk, akkor az objektumok gyártása lassabban történik, és több memóriát is igényel ez a fajta módszer.Mindezek a JavaScript prototípus szerinti objektumkezeléséből következnek. A fejezet végére érve írtam egy tesztszkriptet. A Firefox böngésző alatt mért eredmények a leírtakat hozták, igaz a két módszer között nem volt számottevő különbség. De figyelembe véve, hogy a
prototype
használatával kevesebb memóriát fogyasztunk, könnyebben módosítható objektumokat kapunk, és nem utolsó sorban, objektum literál alkalmazásával átláthatóbb a kódunk, ezért a prototype
tulajdonság használata javasolt.A prototípus lánc és a konstruktor
Befolyásolja-e a prototípus lánc továbbfűzését, ha a fölső objektumnál nem használjuk aprototype
tulajdonságot, és a konstruktorban a this
kulcsszóval hozzuk létre a tulajdonságokat? Ha figyelmesen olvastuk el a tulajdonságok megosztása részt, akkor a választ már tudjuk, ezért mindenféle magyarázat helyett, íme egy példakód://Person konstruktor
function Person(familyName, foreName){
//Minden tulajdonságot itt adjuk meg, nem prototípussal.
this.name = [familyName, foreName];
this.status = 1;
this.setName = function(familyName, foreName){
this.name = [familyName, foreName];
};
this.getName = function(){
return this.name[0] + " " + this.name[1];
};
}
var person1 = new Person("Csavardi", "Samu");
var person2 = new Person("Zsákos", "Frodó");
//VIP konstruktor
function VIP(familyName, foreName){
this._proto_.constructor.call(this, familyName, foreName);
this.status = 2;
}
//Kapcsolódás a Person prototípus láncához.
VIP.prototype = new Person();
VIP.prototype._proto_ = Person.prototype;
//VIP objektum létrehozása
var person3 = new VIP("Szürke", "Gandalf");
alert( person3.getName() );
Object.prototype
|
- Person.prototype
|
- person1
|
- person2
|
- VIP.prototype
|
- person3
|
- Person.prototype
|
- person1
|
- person2
|
- VIP.prototype
|
- person3
Az ábra semmiben nem különbözik a prototípus lánc leírásánál szereplő ábrától. A lényeges különbség, ami nem látszik az ábrán, hogy a
person3
számára a getName metódust nem a Person.prototype
, hanem az VIP.prototype
szolgáltatja. Ha valamiért a VIP.prototype
mintadarabot sem akarjuk használni, akkor a konstruktorban kell átmásolnunk a Person.prototype
objektum tulajdonságait. Ebben az esetben a person3
objektum nem kapcsolódna a Person.prototype
prototípus lánchoz, így annak módosításai nem lennének hatással a létrehozott person3 objektumra.Szabvány metódusok és tulajdonságok
Hasznos lehet néhány beépített tulajdonsággal és metódussal megismerkednünk, amik segítségül jöhetnek az objektumok kezelésekor. Az itt felsorolt tulajdonságok részét képezik azObject.prototype
objektumnak, azaz minden egyéb objektum is rendelkezik ezekkel a tulajdonságokkal és metódusokkal. De bármelyik objektumhoz készíthetünk sajátot, amivel elfedhetjük az alapértelmezettet.constructor
Az objektumok constructor
tulajdonsága az adott objektum konstruktorát adja meg. Minden függvény prototype.constructor
jellemzője automatikusan beállításra kerül, akkor amikor a függvény létrejön. A tulajdonsággal hasonló ellenőrzéseket tudunk elvégezni az objektumokon:
//Függvény a dátum típus ellenőrzéséhez
function isDate(d){
return d && d.constructor && d.constructor == Date;
}
//Metódus az Object prototype bővítésével
Object.prototype.isDate = function(){
return this.constructor == Date;
}
constructor
tulajdonsága, például primitív típusú változók esetén nincs. Majd megvizsgáljuk, hogy a constructor
egyenlő-e a Date
függvénnyel. Az Object prototype
bővítésekor ezeket az ellenőrzéseket elhagyhatjuk.Fontos megjegyezni, hogy ha az objektum leírásakor a
prototype
tulajdonságot objektum literállal adjuk meg, akkor fölülírjuk a konstruktor definiálásakor automatikusan meghatározott értéket, és a constructor
az objektum literál konstruktorát, az Object
konstruktort fogja visszaadni. Ekkor nekünk kell megadnunk a megfelelő értéket. Ezen kívül ugyanilyen felülírás történik a prototípus lánc továbbfűzésekor is.
//A constructor tulajdonság meghatározása.
Foo.prototype.constructor = Foo;
//Vagy objektum literállal.
Foo.prototype = {
constructor : Foo
}
isPrototypeOf
A prototype
objektumok isPrototypeOf
metódusával el tudjuk dönteni, hogy a paraméterül átadott objektum hozzákapcsolódik-e az adott prototípus lánchoz, vagy sem. Hasonló metódusokat tudunk például készíteni:Object.isHTMLElement = function(){
//IE alatt nincs HTMLElement objektum, a metódus nem fog jól működni.
return HTMLElement.prototype.isPrototypeOf(this);
}
HTMLElement
által indított prototípus lánchoz, akkor igaz értékkel tér vissza. A JavaScript alap objektumok, a weboldal szövegei, megjegyzései és a document
objektum sem kapcsolódik a HTMLElement
prototípus láncához.hasOwnProperty
Az objektumok hasOwnProperty
metódusa visszaadja, hogy a megadott tulajdonságnév az adott objektumhoz tartozik-e. Nem vizsgálja a prototípus láncot, csak magát az objektumot. Ha tudjuk, hogy az adott tulajdonság létezik a prototípus láncban, akkor a hasOwnProperty
metódussal megvizsgálhatjuk, hogy az objektum elfedi-e a tulajdonságot, vagy a prototípus láncban fellelhetőt használja.Object.isPrototypeProperty = function(propertyName){
//Nem az aktuális objektum saját tulajdonsága és definiált tulajdonság.
return !this.hasOwnProperty(propertyName) && this[propertyName] !== undefined;
}
valueOf
Amikor egy objektum kifejezésben szerepel, akkor implicit módon az objektum valueOf metódusa kerül meghívásra, hogy a JavaScript megkapja azt a primitív értéket, amit a kifejezés kiértékeléséhez használhat. Azon kívül, hogy az objektumainkhoz is definiálhatunk saját valueOf metódust, átírhatjuk a beépített objektumokét is. A példában felülírjuk a Date.prototype.valueOf
metódusát. Ezután, ha kifejezésben használunk egy dátumot, akkor a saját metódusunk által visszaadott Julian date értékkel fog számolni a JavaScript.Date.prototype.valueOf = function(){
return this.getJD();
}
//Dátum objektumok előállítása
var d1 = new Date;
var d2 = new Date(d1.getFullYear(), 0, 1);
//Január elseje óta eltelt napok száma.
alert( d1 - d2 );
Number
típusú objektumok valueOf
metódusának módosításával. Bármilyen értéket is tartalmaz a Number
objektum, a visszatérési értéke 5/2 lesz.Number.prototype.valueOf = function(){
return 5/2;
}
var n1 = 2;
var n2 = new Number(2);
//Kétszer kettő néha öt.
alert( "2×2 = " + (n1*n2) );
toString
Az objektumok toString
metódusa mind működésében, mind használatában hasonlít a valueOf
metódusra, de akkor kerül meghívásra, ha az objektum primitív értékére szövegként van szükség. A metódus kiírásnál vagy sztringek összefűzésénél kerül implicit módon meghívásra.
Date.prototype.toString = function(){
return this.getFullYear() + "." + (this.getMonth()+1) + "." + this.getDate() + ".";
}
//Az aktuális dátum év.hónap.nap. formátumban.
alert( new Date );
Date.prototype.toString
metódus átírása után, kiíratásnál a dátum magyar formátumban fog megjelenni. De természetesen egyéb, számunkra kedvezőbb formátumot is kreálhatunk, és ahogy a valueOf
metódus esetén, az objektumainkhoz is definiálhatunk saját toString
metódust. További próba a Number
objektummal:Number.prototype.valueOf = function(e){
return this.toFixed() / 2;
}
Number.prototype.toString = function(){
return this / 2;
}
//Mitirki?
alert( new Number(3) );
Néhány fontosnak vélt megjegyzés
Objektumok, metódusok és a this
A JavaScript nyelvben – és ezen már nem is csodálkozunk – a this
sem ugyan azt jelenti, mint a többi nyelvben. Az ok szintén az, hogy a nyelv feladata egy már kész kiszolgáló környezetben maga a kiszolgáló környezet módosítása. Normál objektumorientált programozási nyelvben a this
az osztály definíciójában az aktuális objektum, az osztálypéldány azonosítására szolgál. Mivel a JavaScriptben sem osztály, sem osztálypéldány sincs, így a this
sem alkalmas az osztálypéldány azonosítására.A
this
objektumot jelöl, de az értéke az aktuális futási környezetet jelenti. A this
értéke attól függ, hogy az éppen futó kódot milyen környezetben hívtuk meg. A konstruktorban a new
operátor használata miatt a this
az előállítandó objektumnak felel meg. De egyáltalán nem biztos, hogy a metódusban a metódus objektumával egyenlő. A this
értékét a metódusban is a hívási környezet fogja meghatározni. A Date.prototype
bővítésénél biztosra mehettünk, mert a getJD
metódust a következő módon hívhatjuk meg:var d = new Date;
d.getJD();
.
) operátorral meghatároztuk a hívási környezetet. Az minden esetben a d
változó által megadott Date
objektum. Ha a változó nem Date
típusú, akkor nincs getJD
metódusa sem, és nem kerül végrehajtásra.Nézzünk egy másik példát, amihez hasonlókkal könnyen találkozhatunk. A weboldalakon elég gyakran előfordul, hogy egy területre kattintva egy másik terület tartalmát szeretnénk megjeleníteni vagy elrejteni. Természetesen létrehozhatunk függvényt is erre a feladatra, de sokkal hajlékonyabb megoldáshoz jutunk, ha eddigi ismereteinkre támaszkodva készítünk egy objektumot, ami összekapcsolja az egyik objektum eseményét a másik objektumon végzendő művelettel.
displayChanger.prototype = {
//Jelenleg egy metódus tartozik az objektumhoz, amelyik elrejti-megjeleníti a tartalmat.
changeDisplay : function(){
this.content.style.display = this.content.style.display == 'none'? 'block':'none';
}
}
//A konstruktor két paramétert vár. Azt, amire kattinthatunk label-nek neveztük el,
//az elrejtendő/megjelenítendő tartalom neve pedig content lett.
function displayChanger(label, content){
//Eltároljuk a címke és a tartalom objektumokat.
this.label = document.getElementById(label);
this.content = document.getElementById(content);
//Végül azt mondjuk a konstruktorban, hogy ha kattintanánk a label elemen, akkor hajtsuk végre a changeDisplay metódust.
this.label.onclick = this.changeDisplay;
}
//Az előbb leírt objektumtípus használata, a címkék és a tartalom összerendelése.
new displayChanger("picture1", "menu1");
new displayChanger("picture2", "menu2");
Az ok pedig az, hogy a
changeDisplay
metódus futásakor a this
nem a displayChanger
típusú objektumot fogja jelenteni, hanem a hívó felet. Azt, amelyikre éppen kattintottunk. Amikor értéket adtunk az onclick
tulajdonságnak, akkor a hivatkozott metódus, mint függvény másolódik be a tulajdonság értékeként, és a click
eseményre az a függvény fog végrehajtódni. Az egyik szemünk sír, a másik meg nevet. Mert bár a metódusban így nem kell találgatni, hogy melyik elemre kattintottunk, mivel a this
ezt adja meg, de nem tudjuk megmondani, hogy melyik objektumnak kellene megváltoztatni a megjelenését. Hibajelzést kapunk, mert a picture1
és a picture2
elemeknek nincs content
tulajdonsága.Az egyik megoldás az, hogy a konstruktorban készítünk egy burkoló függvényt, ami – a változók láthatóságának kihasználásával – megoldja, hogy a
changeDisplay
metódusban a this
a létrehozott displayChanger
objektumot jelentse.function displayChanger(label, content){
this.label = document.getElementById(label);
this.content = document.getElementById(content);
this.label.onclick = changeWrapper(this);
function changeWrapper(obj){
return function(){ obj.changeDisplay() }
}
}
changeWrapper
függvény egy nem nevesített függvénnyel tér vissza. A paraméterül kapott obj
objektumot a konstruktor határozza meg, aminek az értéke az aktuálisan létrehozandó objektum lesz. A HTML elemen való kattintáskor ez a nem nevesített függvény fog végrehajtódni, ami semmi mást nem csinál, csak meghívja a konstruktor által megadott objektum changeDisplay
metódusát. Ekkor a metódusban a this
a nekünk megfelelő objektumot fogja jelenteni. A burkoló függvény használatával biztosíthatjuk a this
megfelelő értékét, és paramétereket is átadhatunk a metódusnak.Természetesen a problémára egyéb megoldás is adódhat. Nem csak ilyenféle burkoló függvények használatával oldható meg ez a jelenség. Ezzel a megoldással a saját elképzelésünket erőltetjük a JavaScriptre. Egy másik megoldás lehet az, hogyha a kattintás pillanatában ismerjük a hívó elemet, akkor arra az elemre aggatjuk a többi tulajdonságot, és a kattintáskor lefutó függvény olvassa ki onnan, hogy melyik elemet kell megváltoztatni.
Tanulmányozáshoz itt egy bővebb változata az objektumnak, ami a megadott sütibe elmenti az állapotot, és az objektumok létrehozásakor visszaállítja a felhasználó számára az oldal legutóbbi kinézetét.
function changeContent(label, content, cookieName){
this.label = document.getElementById(label);
this.content = document.getElementById(content);
this.cookieName = cookieName;
addEvent(this.label, 'click', changeWrapper(this));
function changeWrapper(obj){ return function(){ obj.change() }}
if (this.getSavedStatus() == this.SHOW){ this.changeDisplay() }
}
changeContent.prototype = {
HIDE : -1,
SHOW : 1,
change : function(){
this.changeDisplay();
this.saveStatus();
},
changeDisplay : function(){
try {
this.content.style.display = this.content.style.display=='none'? 'block':'none';
}catch(er){}
},
saveStatus : function(){
if (!this.cookieName){ return; }
var contentStatus = this.content.style.display=='none'? this.HIDE:this.SHOW;
setCookie(this.cookieName, contentStatus, 31536000000);
},
getSavedStatus : function(){
if (!this.cookieName){ return; }
return parseInt(getCookie(this.cookieName))==this.HIDE? this.HIDE:this.SHOW;
}
}
Objektumok, függvények és a statikus változók
A függvényekben definiált változók élettartama a változó definiálásától a függvény futásáig tart. Bár a JavaScript nyelvben astatic
foglalt szó, ennek ellenére nem tudunk statikus változót definiálni, ami két függvényhívás között is megtartaná az értékét. Megoldás lehet, hogy globális változót használunk, ez egészen addig tűnik jó megoldásnak, amíg nem kezdenek szaporodni a függvényeink, és fölül nem írjuk az egyik függvényben egy másik függvény globális változóját.A statikus változóra a választ az objektumok megismerése adja. Tudjuk azt, hogy maguk a függvények is változók,
Function
típusú objektumok. Az objektumokhoz pedig bármilyen tulajdonságot definiálhatunk, aminek az élettartama a definiálástól a függvény létezéséig tart. Így a függvény-objektum meg fogja védeni a „statikus változónkat” a véletlen változtatástól, és az értékét is megőrzi.Mire használhatunk statikus változókat? Például eredmények mentésére, azaz gyorsítótárazásra, ami a JavaScript esetén nem jön rosszul. A következő metódus erre mutat egy példát:
//Adott osztályú elemek keresése, a tagName paraméterek megadása nem kötelező.
document.getElementsByClassName = function(className, tagName){
//Hivatkozás az aktuális metódusra.
var method = this.getElementsByClassName;
//Ha az aktuálisan keresett osztálynév megegyezik az előzővel, nem hajtjuk végre a keresést.
if (!(method.prevClassName === className && method.prevTagName === tagName && method.found)){
//A tulajdonságok definiálása.
method.prevClassName = className;
method.prevTagName = tagName;
method.found = [];
//A HTML elemek kinyerése, és a megfelelő RegExp objektum előállítása.
var
elements = this.body.getElementsByTagName(tagName? tagName:"*"), element,
rx = new RegExp('\\b' + className.replace(/\-/g, '\\-') + '\\b');
//Végiglépkedünk az elemeken, a megfelelő elem hozzáadása a gyorsítótárhoz.
for (var i=0; i < elements.length; ++i){
element = elements[i];
if (element.className && rx.test(element.className)){
method.found.push(element);
}
}
}
//Visszatérünk a talált elemekkel, amiket a metódus a következő hívásig megjegyez.
return method.found;
}
getElementsByClassName
metódus megjegyzi az előzőleg talált elemeket, így nem kell végiglépkedni a weboldal összes elemén, ha újra ugyanahhoz az osztályhoz tartozó elemeket keressük. A második hívásra az eredményt majdhogynem azonnal megkapjuk. Jelentős teljesítménynövekedést érhetünk el a gyorsítótárazás megoldásával. Ezen kívül már könnyen megoldhatjuk, hogy következő kérésnél a találatokat fűzze hozzá az előzőhöz. Statikus változók a JavaScriptben nincsenek; csak objektum tulajdonságokat használunk úgy, mintha statikus változók lennének.Objektumok, sztringek és a JSON
AJAX technika alkalmazásakor különféle típusú adatokat szeretnénk mind küldeni, mind fogadni, de az adatokat szöveges formátumba kell öltöztetni, hogy továbbítani tudjuk. Erre nyújt egy megoldást a JSON leírás. Amit – most már ismerve a JavaScript objektumkezelését – könnyen megérthetünk. Az elnevezés is erre utal: JavaScript Object Notation ([i]JavaScript Objektum Jelölés).A JSON leírás azt mondja, hogy az elküldendő adatokat írjuk le a JavaScriptben használatos objektum vagy tömb literállal. Majd ezt a literált küldjük el szövegként, amit majd a fogadó értelmezni fog. Annyi megszorítást tesz, hogy objektumok esetén a név-érték pároknál a nevet kettős idézőjelek (") közzé kell tenni, az értékek pedig a következők lehetnek: kettős idézőjelekkel határolt sztring, szám,
true
, false
vagy null
érték, illetve további ennek a leírásnak megfelelő objektum vagy tömb literál. Ezeket a megszorításokat azért alkalmazza, hogy a fogadó fél a sztring értelmezése után biztosan azt az objektumot vagy tömböt kapja meg, amit elküldtünk.//Az előállított objektum szerkezete.
{
first_item : '5',
second_item : undefined,
third_item : [5, 7, 37, 52]
}
//A továbbítandó szöveg, ami az objektum literált tartalmazza.
'{"first_item":"5","second_item":null,"third_item":[5,7,37,52]}'
eval
függvénnyel értelmeznünk kell. Azaz a fogadó oldalon végre kell hajtani egy ismeretlen programkódot. Ami jó esetben csak egy objektum literált tartalmaz, rossz esetben nem. A JSON leírás a következő metódust adja a szöveg ellenőrzéséhez:String.prototype.parseJSON = function(){
try {
return
!(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
this.replace(/"(\\.|[^"\\])*"/g, ''))) && eval('(' + this + ')');
}catch(e) {
return false;
}
}
true
, false
, null
, objektum literál: {}
, tömb literál: []
, számok jelölése és elválasztójelek. Betörő legyen a talpán, aki ezekből a megmaradt karakterekből értelmes kódot akar előállítani. Mindenesetre a föntihez hasonló ellenőrzést a szövegben lévő adatszerkezet előállítása előtt alkalmaznunk kell szerver és kliens oldalon is.Összefoglalás
Ezzel véget ért a JavaScript objektumkezelésével való ismerkedésünk. Remélem, sokak hasznos leírásnak fogják találni, és Felhő cikkével együtt többen megfelelő alapot szerezhetnek a JavaScript objektumorientált programozásához. Akár a nulláról az ötödik vagy hetedik szintre :) tudnak eljutni. De a cikk végére, ha böcsületesen elolvastad, a következő kérdésekre mindenképpen meg tudod adni a helyes választ:- Mi ez a sok hülye zárójel?
A kapcsos zárójel ({}
) nem csak blokk és utasítások zárójelezésre, hanem objektum literál jelölésére is használatos.
- Mi az a prototípus szerinti objektumkezelés?
A prototípus szerinti objektumkezelés alapja az, hogy minden objektum egy belső hivatkozást tartalmaz a mintadarabjára.
- Hogyan lehet osztályt létrehozni?
Nem lehet létrehozni, mert nincs. Egy objektum jellemzőit vagy a konstruktor határozza meg, vagy a prototípus láncban látható objektumok tartalmazzák. Az objektum leírása nincs elkülönítve az objektumtól. Minden objektum egyGlobal
típusú objektum része.
- Hogyan lehet objektumot létrehozni?
Objektum literállal, vagynew
operátorral beépített vagy saját konstruktor hívásával.
- Hogyan lehet tulajdonságot definiálni?
Mivel a JavaScript nem típusos nyelv, ezért egy tulajdonság létrehozásához elegendő értéket adnunk a tulajdonságnak. Többféle módon is definiálhatunk tulajdonságot, például a konstruktorban a pont (.
) operátorral. A prototípus láncban az adott objektum felől látható részen bárhol definiált tulajdonság az objektum része lesz.
- Hogyan lehet metódust definiálni?
Ugyanúgy, ahogy tulajdonságot. A metódusokFunction
típusú tulajdonságok.
- Mire való a
prototype
tulajdonság?
A tulajdonságok megosztására és az örökélés megvalósítására. Ez utóbbi is az előzővel valósul meg.
A cikkben szereplő kódokat letöltheted, ha nem szeretnél foglalkozni a gépelésükkel.
még még még ennyi nem elég :)
Első kettőre egy gyors példa :)
ui: természetesen, részemről nagyon nagy köszönet a fenti cikkért
Re: még még még
A Weblabor szerkesztői szívesen veszik Tőled is a cikkeket. Tessék megírni, és lesz! A példádból látszik, hogy számos dolgot meg lehet még oldani a JavaScriptben. Ahogy fölvetetted, érdemes lehet erről is írni.
Akkor érdemes volt elolvasni?
Douglas Crockford — "Advanced JavaScript"
Douglas Crockford — "Advanced JavaScript"
http://developer.yahoo.com/yui/theater/ (2. oszloban)
üdv t
Douglas Crockford előadások
De az előadásokat nem is láthattam volna, a cikk még tavaly ősszel készült, csak a weblaborosok most jutottak odáig, hogy publikálni tudták, az előadások meg 2007. januárjában hangoztak el. Az első két előadást megnézve, mindenkinek csak ajánlani tudom, hogy tekintse meg őket, 0-tól 99 éves korig. Az meg csak engem nyugtat meg, hogy Crockford előadása hasonlóan épül fel, mint a cikkem. :) - Ezzel nem azt akarom mondani, hogy bátorkodnám hozzá mérni magam. Csupán azt, hogy valószínűleg hasonlóan gondolkodunk a programozási nyelvekről.
Köszönet a linkért! Sokaknak hasznos "mozi" lesz!
Én láttam…
Szép
Ha már volt postolt kód, akkor had küldjek be én is egy (nem túl szép) megoldást:
Köszi!
this.constructor
Adj értéket a Szemely prototype tulajdonságnak. Mindjárt nem lesz jó, amit írtál.
Kiigazítás
Ugyan ez történik a prototípus lánc továbbfűzésekor. Az ebből leszármaztatott objektumok mind ugyanazt fogják tartalmazni, ahogy literál használatakor felűlírásra kerül az eredeti érték.
gratula!
szuper cikk, én nem vagyok túlságosan jártas JS-ben, de ebből nagyon sokat tanultam. Köszi a rászánt időt és a publikációt.
Minden jót!
Köszi!
Akkor az eddigi hozzászólásból levonhatom, hogy a bevezetőben kitűzött célt sikerült elérni.
grat;)
Üdv,
SamY
:)))
Kösz! És örülök, hogy segített a cikk!
Globális-Lokális változók
van mondjuk egy ilyen kód:
és igazából ez a legnagyobb hibája az egész prototypeos módszernek, ezért nem szívesen használom saját class-eknél, mert a lokális változókat nem tudja kezelni sehogy sem.
szóval mi történik ilyenkor a local-lal???
(ami fura az egészben, hogy ha globális változót kérne le ilyenkor a prototypeba rakott függvény, akkor hibaüzenet jönne, viszont a localnak minden létrehozott object esetén másnak kéne lennie, szóval ez így alapból teljesen logikátlan nekem)
megoldás
-elsőre létrehozza a prototypeot a konstruktorral, és a private változókat is deklarálja ugyanúgy, mint a publicokat a prototypeban.
-utána ha bmilyen módosítás történik, akkor azt már a létrehozott objecten hajtja végre, és nem a prototypeban, tehát ha mondjuk egy private változót módosítunk egy a konstruktorban elhelyezett this-es fgvel, akkor létrejön egy private változó (feltéve ha létezett a prototypeban is olyan private változó (ha nem, akkor globalnak veszi a javascript, mert ugyanolyan szintaxissal hivatkozunk rá), majd ennek az értéke változik. ha viszont a prototype egy függvényéből módosítjuk az adott változót, akkor a prototypeban módosul a private típusú változó, és az összes objectnek, aminek nem lett vmilyen konstruktoros függvénnyel megváltoztatva az adott változója módosul az érték..
tehát a példában a local-lal az történik, hogy a prototypeból szedi le a prototype private változóját és nem az adott object private változóját kéri le.
nah most szerintem ez így elég nagy bugnak számít, mert ha megnézzük a public változókat, akkor azokat a prototypeban megadott függvények az adott objecten módosítják, és nem a prototypeban
adok kódot, amivel végülis teszteltem:
Változók láthatósága és élettartama
Egy függvényblokkon belül a változók láthatósága a függvényblokkra korlátozódik. De minden, az adott blokkon belül definiált függvény látni fogja a blokk változóit. Mivel további hivatkozás is van a változóra, ezért az továbbra is élni fog, de csak az adott függvényeken belül látható. - (Megjegyzés: A JavaScript csak függvényblokkot ismer, ellentétben a C-vel. Az utasításoknál használható kapcsos zárójelek utasítás zárójelek, nem blokkok, ahogy a Pascalban a begin...end páros.)
A Class függvény minden esetben egy függvénnyel tér vissza, ezért a hívásakor a new használata felesleges. A Class futásakor a new_class változó csak értéket kap, de nem fut le, hisz nem kerül meghívásra.
A new_class függvényben a
arguments.callee.prototype
anew_class.prototype
kifejezésnek felel meg. Azért kell a new_class függvényben definiálni a getLocal és setLocal függvényeket, hogy láthassák a konstruktorban megadott local változót.A new_class konstruktor hívásakor a new_class.prototype.setLocal metódus szintjén is látható lesz a local változó, de minden egyes konstruktor híváskor az felülíródik, mivel a változónevek egyediek. Ez a felülírás a this.setLocal használatával értelemszerűen nem történik meg.
Remélem, érthető volt! Nem néztem pontosan utánna a dolgoknak, ha valahol tévedtem Felhő minden bizonnyal ki fogja egészíteni a leírtakat.
Azt hittem érthető volt a cikkből, hogy a szokásos OOP ráerőltetése a JavaScriptre felesleges. Megvan a JavaScript saját, prototípus szerinti objektumkezelése.
OK
Viszont van másik kérdésem, ami kicsit egyszerűbb:
jah, másik érdekesség, most találtam, firefox core javascript bug:
Konstruktor és a prototípus lánc
Bocs, hogy kicsit későn válaszolok, most jutott rá időm. Egyébként is az ilyen kérdéseket talán Felhőhöz kellene intézned! :) Azért
undefined
mert a prototípus lánc nem a konstruktorban felülírtprototype
tulajdonságra fog mutatni. Tanulság: ne írd fölül a prototípust a konstruktorban!Kösz
+3 adattípus?
Még régen olvastam a cikket, és ez adta a lökést hogy megismerjem a js oop "oldalát", de azóta se tudom mi ez a további 3 adattípus. Valaki le tudná írni a nevüket hogy rá tudjak keresni?
Jó kérdés
Én valahogy úgy értettem hogy