Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis treci
2 O autorze ............................................................................................................................................... 15
2 Wstp .................................................................................................................................................... 17
Rozdzia 1. Czym jest JavaScript? ..........................................................................................................23
Krtka historia ............................................................................................................. 24
Implementacje JavaScriptu ........................................................................................... 25
ECMAScript ............................................................................................................ 25
Model DOM ............................................................................................................ 28
Model BOM ............................................................................................................ 31
Podsumowanie ............................................................................................................ 32
Spis treci
Spis treci
Spis treci
10
Spis treci
11
Dziedziczenie
Prawdziwie obiektowy jzyk programowania musi obsugiwa dziedziczenie, czyli moliwo
korzystania (dziedziczenia) z metod i waciwoci jednej klasy przez inn klas. W poprzednim
rozdziale nauczye si definiowa waciwoci i metody klasy. Czasem chcemy, by dwie
rne klasy mogy korzysta z tych samych metod. Wtedy wanie przydaje si dziedziczenie.
Dziedziczenie w praktyce
Najprostszym sposobem na opisanie dziedziczenia jest posuenie si klasycznym przykadem figurami geometrycznymi. Tak naprawd istniej dwa typy figur paskich: elipsy
(ktre s okrge) i wielokty (ktre maj pewn ilo bokw). Koo to rodzaj elipsy z jednym ogniskiem; trjkty, czworokty i piciokty to rodzaje wieloktw z rn iloci
bokw. Kwadrat to rodzaj czworokta z wszystkimi bokami rwnymi. Jest to idealny przykad powizania dziedzicznego.
W przykadzie tym figura (Shape) jest klas bazow (wszystkie klasy s jej potomkami) dla klas elipsa (Ellipse) i wielokt (Polygon). Elipsa ma jedn waciwo zwan
ogniska (foci) wskazujc ilo ognisk elipsy. Koo (Circle) jest potomkiem elipsy, wic
nazywamy je podklas elipsy, a sama elipsa jest nadklas dla koa. Podobnie trjkt
(Triangle), czworokt (Rectangle) i piciokt (Pentagon) s podklasami wielokta,
a wielokt jest nadklas dla kadej z tych klas. Wreszcie kwadrat (Square) jest potomkiem czworokta.
Powizania dziedziczne najlepiej opisa przy uyciu diagramu w tym momencie pomocny okazuje si uniwersalny jzyk modelowania, czyli UML. Jednym z wielu przeznacze UML jest wizualna reprezentacja zoonych powiza midzy obiektami, takich
jak dziedziczenie. Na rysunku 4.1 wida diagram UML opisujcy zwizek klasy Shape z jej
podklasami.
130
Rysunek 4.1.
W UML kady prostokt reprezentuje klas opisan nazw. Linie biegnce od wierzchu trjkta, czworokta i piciokta zbiegaj si i wskazuj na figur, pokazujc, e kada z tych
klas jest potomkiem figury. Podobnie strzaka biegnca od kwadratu do czworokta symbolizuje powizanie dziedziczne pomidzy tymi klasami.
Wicej na temat UML mona przeczyta w ksice Instant UML (Wrox Press,
ISBN 1861000871).
Implementacja dziedziczenia
Aby zaimplementowa dziedziczenie w jzyku ECMAScript, zaczynamy od klasy bazowej
dla wszystkich potomkw. Kandydatami na klasy bazowe s klasy stworzone przez programist. Ze wzgldw bezpieczestwa obiekty wasne i obiekty hosta nie mog by klasami
bazowymi. Zapobiega to publicznemu udostpnieniu skompilowanego kodu poziomu przegldarki, ktry mgby potencjalnie zosta uyty w zych zamiarach.
Po wybraniu klasy bazowej mona przej do tworzenia podklas. To, czy klasa bazowa bdzie w ogle uywana, zaley wycznie od nas. Czasami pojawia si potrzeba stworzenia
klasy bazowej, ktra nie ma by wykorzystywana bezporednio. Zamiast tego udostpnia
ona jedynie wsplne podklasom cechy funkcjonalne. W takich okolicznociach klas bazow
uwaa si za abstrakcyjn.
Rozdzia 4.
Dziedziczenie
131
Sposoby dziedziczenia
Jak zwykle w jzyku ECMAScript mona implementowa dziedziczenie na kilka sposobw. Wynika to z faktu, e dziedziczenie w JavaScripcie nie jest jawne, tylko emulowane.
Oznacza to, e interpreter nie obsuguje wszystkich szczegw zwizanych z dziedziczeniem. Zadaniem dla programisty jest obsuga dziedziczenia w sposb najbardziej adekwatny do okolicznoci.
Maskowanie obiektw
O maskowaniu obiektw nie mylano jeszcze, kiedy opracowywano pierwsz wersj ECMAScriptu. Koncepcja ta ewoluowaa, w miar jak programici coraz lepiej rozumieli, jak
tak naprawd dziaaj funkcje i, w szczeglnoci, jak posugiwa si sowem this w kontekcie funkcji.
Rozumowanie jest nastpujce: konstruktor przypisuje wszystkie waciwoci i metody (przy
deklarowaniu klas wzorcem konstruktora) sowem this. Poniewa konstruktor to po prostu
funkcja, mona uczyni konstruktor klasy ClassA metod klasy ClassB i j wywoa. ClassB
zostanie wwczas wyposaona we wszystkie waciwoci i metody zdefiniowane w konstruktorze klasy ClassA. Na przykad, klasy ClassA i ClassB mona zdefiniowa nastpujco:
function ClassA(sColor) {
this.sColor = sColor;
this.sayColor = function () {
alert(this.sColor);
};
}
function ClassB(sColor) {
}
Jak zapewne pamitasz, sowo kluczowe this wskazuje na bieco tworzony w konstruktorze obiekt. Jednak w metodzie this wskazuje obiekt, do ktrego metoda naley. Zgodnie z
omawian teori stworzenie klasy ClassA jako normalnej funkcji, a nie jako konstruktora,
tworzy pewien rodzaj dziedziczenia. Mona to zrobi w konstruktorze klasy ClassB tak:
function ClassB(sColor) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
}
132
Jeeli na przykad istniej dwie klasy ClassX i ClassY, a chcemy, by klasa ClassZ bya potomkiem obu tych klas, to moemy napisa:
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
Rozdzia 4.
Dziedziczenie
133
this.newMethod();
delete this.newMethod;
Minusem jest to, e jeli klasy ClassX i ClassY maj waciwoci lub metody o tej samej nazwie, to ClassY ma pierwszestwo, poniewa dziedziczenie cech po niej nastpuje pniej.
Poza tym drobnym problemem dziedziczenie wielokrotne poprzez maskowanie obiektw
jest proste.
Poniewa te metoda dziedziczenia zrodzia si w praniu, trzecia edycja ECMAScriptu
wprowadza dwie nowe metody obiektu Function: call() i apply().
Metoda call()
Metoda call() jest najbardziej podobna do klasycznej metody maskowania obiektw. Jej
pierwszy argument to obiekt, ktry ma wskazywa this. Wszystkie pozostae argumenty s
przekazywane bezporednio do samej funkcji. Na przykad:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.sColor + sSuffix);
};
var oObj = new Object();
oObj.sColor = "czerwony";
// wywietla "Kolorem jest czerwony. Naprawd adny kolor. "
sayColor.call(oObj, "Kolorem jest ", ". Naprawd adny kolor. ");
W tym przykadzie funkcja sayColor() zostaa zdefiniowana poza obiektem i wskazuje na sowo this, mimo e nie zostaa zwizana z adnym obiektem. Obiektowi oObj nadano waciwo
sColor o wartoci "czerwony". W wywoaniu call() pierwszym argumentem jest oObj, co
wskazuje, e sowo this w obrbie sayColor() powinno wskazywa warto oObj. Drugi
i trzeci argument to cigi znakowe. Odpowiadaj one argumentom sPrefix i sSuffix funkcji
sayColor(), w efekcie wywietlony zostaje tekst "Kolorem jest czerwony. Naprawd adny
kolor. ".
Aby wykorzysta to w schemacie dziedziczenia poprzez maskowanie obiektw, wystarczy
zastpi trzy wiersze, ktre przypisuj, wywouj i usuwaj now metod:
function ClassB(sColor, sName) {
// this.newMethod = ClassA;
// this.newMethod(sColor);
// delete this.newMethod;
ClassA.call(this, sColor);
this.sName = sName;
this.sayName = function () {
alert(this.sName);
};
}
W tym przypadku chcemy, by sowo kluczowe this w ClassA odpowiadao nowo utworzonemu obiektowi ClassB, przesyamy je wic jako pierwszy argument. Drugi argument to
sColor, tylko jeden dla kadej z klas.
134
Metoda apply()
Metoda apply() pobiera dwa argumenty: obiekt, ktry ma wskazywa this i tablic argumentw do przesania do funkcji. Na przykad:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.sColor + sSuffix);
};
var oObj = new Object();
oObj.sColor = "czerwony";
// wywietla "Kolorem jest czerwony. Naprawd adny kolor. "
sayColor.apply(oObj, new Array("Kolorem jest ",". Naprawd adny kolor. "));
Przykad jest taki sam jak poprzednio, ale tym razem wywoywana jest metoda apply(). W wywoaniu pierwszym argumentem pozostaje oObj, ktry dalej wskazuje, e sowo this w funkcji
sayColor() ma mie przypisan warto oObj. Drugi argument to tablica skadajca si z dwch
cigw, ktre odpowiadaj argumentom sPrefix i sSuffix funkcji sayColor(). Efektem jest
ponownie wywietlenie tekstu "Kolorem jest czerwony. Naprawd adny kolor. ".
T metod rwnie mona uy w miejsce trzech wierszy przypisujcych, wywoujcych
i usuwajcych now metod:
function ClassB(sColor, sName) {
// this.newMethod = ClassA;
// this.newMethod(sColor);
// delete this.newMethod;
ClassA.apply(this, new Array(sColor));
this.sName = sName;
this.sayName = function () {
alert(this.sName);
};
Ponownie przesyamy this jako pierwszy argument. Drugi argument to tablica z tylko jedn
wartoci sColor. Zamiast tego, moemy jako drugi argument metody apply() przesa
cay obiekt arguments klasy ClassB:
function ClassB(sColor, sName) {
// this.newMethod = ClassA;
// this.newMethod(sColor);
// delete this.newMethod;
ClassA.apply(this, arguments);
this.sName = sName;
this.sayName = function () {
alert(this.sName);
};
Rozdzia 4.
Dziedziczenie
135
Podobnie jak przy maskowaniu, wszelkie nowe waciwoci i metody podklasy musz by
definiowane po przypisaniu waciwoci property, poniewa wszystkie metody przypisane
wczeniej zostan usunite. Dlaczego? Jako e waciwo property jest w caoci zastpowana nowym obiektem, pierwotny obiekt, do ktrego dodawalimy metody, jest niszczony. Tak wic kod dodajcy waciwo sName i metod sayName() do klasy ClassB powinien wyglda tak:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.sName = "";
ClassB.prototype.sayName = function () {
alert(this.sName);
};
136
Dodatkowo przy wizaniu acuchowym prototypw operator instanceof dziaa w do unikalny sposb. Dla wszystkich egzemplarzy ClassB operator instanceof zwraca true zarwno
dla ClassA, jak i dla ClassB. Na przykad:
var oObjB = new ClassB();
alert(oObjB instanceof ClassA);
alert(oObjB instanceof ClassB);
// wywietla "true"
// wywietla "true"
W wiecie lunej kontroli typw, jaka obowizuje w jzyku ECMAScript, jest to niezwykle
przydatne narzdzie, ktre nie jest dostpne, gdy posugujemy si maskowaniem obiektw.
Minusem wizania acuchowego prototypw jest brak obsugi dziedziczenia wielokrotnego. Jak zapewne pamitasz, wizanie acuchowe polega na nadpisaniu waciwoci prototype klasy innym typem obiektu.
Metoda hybrydowa
Dziedziczenie poprzez maskowanie obiektw posuguje si przy definiowaniu klas wzorcem
konstruktora, nie korzystajc w ogle z prototypw. Gwny problem polega tu na tym, e
musimy uy wzorca konstruktora, ktry (o czym przekonae si w poprzednim rozdziale)
nie jest optymalny. Jeeli z kolei zastosujemy wizanie acuchowe prototypw, tracimy
moliwo posugiwania si konstruktorami z argumentami. Jak radz sobie z tym programici? Odpowied jest prosta: stosuj obydwie metody.
W poprzednim rozdziale dowiedziae si, e najlepszy sposb na tworzenie klas polega na
stosowaniu wzorca konstruktora do definiowania waciwoci i wzorca prototypu do definiowania metod. To samo dotyczy dziedziczenia uywamy maskowania do dziedziczenia
waciwoci od konstruktora i wizania acuchowego prototypw, by dziedziczy metody
po obiekcie prototype. Spjrzmy na poprzedni przykad napisany od nowa przy uyciu obydwu metod dziedziczenia:
function ClassA(sColor) {
this.sColor = sColor;
}
ClassA.prototype.sayColor = function() {
alert(this.sColor);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.sName = sName;
Rozdzia 4.
Dziedziczenie
137
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function () {
alert(this.sName);
};
138
Zauwa, e klasa Polygon sama w sobie nie jest na tyle konkretna, by mona jej byo uywa. Metoda getArea() zwraca 0, poniewa peni jedynie rol miejsca na metody podklas,
ktre j zastpi.
Tworzenie podklas
Zastanwmy si teraz nad klas Triangle reprezentujc trjkt. Trjkt ma trzy boki, wic
klasa ta musi zastpi waciwo iSides klasy Polygon i ustali jej warto na 3. Metoda
getArea() rwnie musi by zastpiona, by skorzysta ze wzoru na pole trjkta, ktrym
jest 1/2podstawawysoko. Skd jednak metoda wemie wartoci podstawy i wysokoci?
Poniewa musz zosta wprowadzone konkretne ich wartoci, konieczne jest stworzenie
waciwoci iBase (podstawa) i iHeight (wysoko). Reprezentacja UML-u trjkta widoczna
jest na rysunku 4.4.
Rysunek 4.4.
Diagram ten ukazuje jedynie nowe waciwoci i zastpowane przez klas Triangle metody.
Gdyby klasa Triangle nie definiowaa wasnej wersji getArea(), metoda ta nie zostaaby
wymieniona na diagramie. Byaby traktowana jako odziedziczona po klasie Polygon. Nieco
lepiej wyjania to kompletny diagram UML reprezentujcy powizania midzy klasami
Polygon i Triangle (rysunek 4.5).
Rysunek 4.5.
Rozdzia 4.
Dziedziczenie
139
W diagramach UML nigdy nie powtarza si metod odziedziczonych, chyba e zostay zastpione (lub przeadowane, co akurat w jzyku ECMAScript nie jest moliwe).
Kod klasy Triangle wyglda nastpujco:
function Triangle(iBase, iHeight) {
Polygon.call(this, 3);
this.iBase = iBase;
this.iHeight = iHeight;
}
Triangle.prototype = new Polygon();
Triangle.prototype.getArea = function () {
return 0.5 * this.iBase * this.iHeight;
};
Zwrmy uwag, e konstruktor klasy Triangle przyjmuje dwa argumenty, iBase i iHeight,
mimo e konstruktor klasy Polygon przyjmuje tylko jeden argument iSides. Wynika to z faktu,
e z gry wiadomo ile bokw ma trjkt i nie chcemy, aby programista mg to zmienia.
Wic kiedy uywamy maskowania obiektw liczba 3 jest przesyana do konstruktora Polygon
jako liczba bokw dla tego obiektu. Nastpnie wartoci iBase i iHeight s przypisywane do
odpowiednich waciwoci.
Po zastosowaniu wizania acuchowego prototypw do dziedziczenia metod klasa Triangle
zastpuje metod getArea() wasn, by udostpni wzr na pole trjkta.
Ostatnia klasa to Rectangle, reprezentujca czworokt, ktra rwnie jest potomkiem klasy
Polygon. Czworokty maj cztery boki, a ich pole oblicza si, mnoc dugo przez szeroko, ktre s dwoma waciwociami, jakie musi wprowadzi klasa Rectangle. Na diagramie
UML klasa ta pojawia si obok klasy Triangle, poniewa dla obu tych klas nadklas jest
Polygon (patrz: rysunek 4.6).
Rysunek 4.6.
140
Zwr uwag, e konstruktor klasy Rectangle rwnie nie przyjmuje iSides jako argumentu i ponownie warto staa (4) zostaje przesana wprost do konstruktora klasy Polygon.
Rwnie na podobiestwo Triangle, klasa Rectangle wprowadza dwie nowe waciwoci
jako argumenty dla konstruktora i zastpuje metod getArea() wasn wersj.
Testowanie kodu
Stworzony dla tego przykadu kod klas mona przetestowa, uruchamiajc nastpujcy
przykad:
var oTriangle = new Triangle(12, 4);
var oRectangle = new Rectangle(22, 10);
alert(oTriangle.iSides);
alert(oTriangle.getArea());
// wywietla "3"
// wywietla "24"
alert(oRectangle.iSides);
alert(oRectangle.getArea());
// wywietla "4"
// wywietla "220"
Kod ten tworzy trjkt o podstawie 12 i wysokoci 4 oraz prostokt o dugoci 22 i szerokoci 10. Nastpnie wywietlane s liczby bokw i pola dla kadej figury, aby dowie, e
waciwo iSides zostaa poprawnie ustawiona i e metoda getArea() zwraca stosowne
wartoci. Pole trjkta powinno wynosi 24, a pole prostokta 220.
Co z dynamicznymi prototypami?
W poprzednim przykadzie zastosowano wzorzec hybrydowy konstruktor-prototyp do definiowania obiektw, aby ukaza dziedziczenie, ale czy bdzie to dziaa rwnie z dynamicznymi prototypami? Ot nie.
Dziedziczenie nie dziaa z dynamicznymi prototypami z uwagi na unikatow natur obiektu
prototype. Spjrzmy na nastpujcy kod (ktry jest nieprawidowy, ale mimo to warto go prze-
studiowa):
function Polygon(iSides) {
this.iSides = iSides;
if (typeof Polygon._initialized == "undefined") {
Polygon.prototype.getArea = function() {
return 0;
};
Rozdzia 4.
Dziedziczenie
141
Polygon._initialized = true;
}
}
function Triangle(iBase, iHeight) {
Polygon.call(this, 3);
this.iBase = iBase;
this.iHeight = iHeight;
if (typeof Triangle._initialized == "undefined") {
Triangle.prototype = new Polygon();
Triangle.prototype.getArea = function() {
return 0.5 * this.iBase * this.iHeight;
};
Triangle._initialized = true;
}
}
W powyszym kodzie widzimy klasy Polygon i Triangle zdefiniowane przy uyciu dynamicznych prototypw. Bd tkwi w wyrnionym wierszu, w ktrym tworzony jest Triangle.
prototype. Z logicznego punktu widzenia miejsce tworzenia prototypu jest dobre, ale funkcjonalnie kod nie bdzie dziaa. Dokadnie chodzi o to, e w chwili wykonywania tego
kodu, obiekt ten bdzie ju stworzony i zwizany z oryginalnym obiektem prototype. Mimo e zmiany w obiekcie prototypu zostan uwzgldnione prawidowo dziki mechanizmowi bardzo pnego wizania, zastpienie obiektu prototype nie bdzie miao wpywu
na ten obiekt. Zmiany zostan uwzgldnione tylko w tworzonych pniej egzemplarzach
obiektu, a pierwszy egzemplarz pozostanie niepoprawny.
Aby prawidowo korzysta z dynamicznych prototypw i dziedziczenia, konieczne jest
przypisanie nowego obiektu prototype poza obszarem konstruktora:
function Triangle(iBase, iHeight) {
Polygon.call(this, 3);
this.iBase = iBase;
this.iHeight = iHeight;
if (typeof Triangle._initialized == "undefined") {
Triangle.prototype.getArea = function () {
return 0.5 * this.iBase * this.iHeight;
};
Triangle._initialized = true;
}
}
Triangle.prototype = new Polygon();
Kod ten dziaa, poniewa obiekt prototype zostaje przypisany, zanim powstan jakiekolwiek egzemplarze obiektw. Niestety oznacza to, e kod nie bdzie w peni zhermetyzowany w konstruktorze, a w kocu wanie to jest gwnym celem metody dynamicznych prototypw.
142
zInherit
Wizanie acuchowe prototypw w istocie kopiuje wszystkie metody z obiektu do obiektu
reprezentujcego prototyp klasy (prototype). A moe istnieje inny sposb osignicia tego
efektu? Ot istnieje. Za pomoc biblioteki zInherit (dostpnej pod adresem http://www.
nczonline.net/downloads) moliwa jest realizacja dziedziczenia metod bez korzystania z wizania acuchowego prototypw. Ta niewielka biblioteka obsuguje wszystkie wspczesne
przegldarki (Mozilla, IE, Opera, Safari) oraz niektre starsze (Netscape 4.x, IE/Mac).
Aby mc korzysta z biblioteki zInherit, trzeba doczy plik zinherit.js
znacznikiem <script/>. Doczanie zewntrznych plikw JavaScriptu zostao
omwione szczegowo w rozdziale 5., JavaScript w przegldarce.
Biblioteka zInherit dodaje do klasy Object dwie metody: inheritFrom() i instanceOf().
Metoda inheritFrom() zajmuje si prac fizyczn, kopiujc metody z danej klasy. Oto wiersz
kodu, ktry realizuje dziedziczenie metod klasy ClassA przez klas ClassB, uywajc wizania acuchowego prototypw:
ClassB.prototype = new ClassA();
Metoda inheritFrom() przyjmuje jeden argument, bdcy nazw klasy, z ktrej maj by
skopiowane metody. Zauwamy, e w odrnieniu od wizania acuchowego prototypw
ten wzorzec nie tworzy nawet nowego egzemplarza klasy, z ktrej dziedziczy, sprawiajc
e proces jest bezpieczniejszy, a programista nie musi przejmowa si argumentami dla konstruktora.
Wywoanie metody inheritFrom() musi nastpowa dokadnie tam, gdzie normalnie
nastpuje przypisanie prototypu, aby dziedziczenie mogo dziaa poprawnie.
Metoda instanceOf() zastpuje operator instanceof. Poniewa wzorzec ten nie korzysta w ogle
z wizania acuchowego prototypw, poniszy wiersz kodu nie bdzie dziaa:
ClassB instanceof ClassA
Rozdzia 4.
Dziedziczenie
143
Wielokty kontratakuj
Cay przykad z wieloktami mona przepisa, korzystajc z biblioteki zInherit, zastpujc
w nim tylko dwa wiersze (wyrnione):
function Polygon(iSides) {
this.iSides = iSides;
}
Polygon.prototype.getArea = function () {
return 0;
};
function Triangle(iBase, iHeight) {
Polygon.call(this, 3);
this.iBase = iBase;
this.iHeight = iHeight;
}
Triangle.prototype.inheritFrom(Polygon);
Triangle.prototype.getArea = function () {
return 0.5 * this.iBase * this.iHeight;
};
function Rectangle(iLength, iWidth) {
Polygon.call(this, 4);
this.iLength = iLength;
this.iWidth = iWidth;
}
Rectangle.prototype.inheritFrom(Polygon);
Rectangle.prototype.getArea = function () {
return this.iLength * this.iWidth;
};
Aby przetestowa ten kod, moemy posuy si tym samym przykadem co wczeniej, dodajc kilka dodatkowych wierszy testujcych metod instanceOf():
var oTriangle = new Triangle(12, 4);
var oRectangle = new Rectangle(22, 10);
alert(oTriangle.iSides);
alert(oTriangle.getArea());
alert(oRectangle.iSides);
alert(oRectangle.getArea());
alert(oTriangle.instanceOf(Triangle));
alert(oTriangle.instanceOf(Polygon));
alert(oRectangle.instanceOf(Rectangle));
alert(oRectangle.instanceOf(Polygon));
// wywietla "true"
// wywietla "true"
// wywietla "true"
// wywietla "true"
144
Rozdzia 4.
Dziedziczenie
145
ClassX._initialized = true;
function ClassY() {
this.sMessageY = "To jest komunikat Y.";
if (typeof ClassY._initialized == "undefined") {
ClassY.prototype.sayMessageY = function () {
alert(this.sMessageY);
};
ClassY._initialized = true;
ClassX i ClassY to mae klasy z jedn waciwoci i jedn metod. Powiedzmy, e stworzylimy klas ClassZ, ktra ma by potomkiem obu tych klas. Klas t mona zdefiniowa
nastpujco:
function ClassZ() {
ClassX.apply(this);
ClassY.apply(this);
this.sMessageZ = "To jest komunikat Z.";
if (typeof ClassZ._initialized == "undefined") {
146
xbObject
Strona DevEdge naleca do Netscapea (http://devedge.netscape.com) zawiera wiele przydatnych informacji i narzdzi wspomagajcych pisanie skryptw dla programistw sieci
WWW. Jedno z takich narzdzi to xbObject (mona je pobra pod adresem http://archive.
bclary.com/xbProjects-docs/xbObject/), napisane przez Boba Claryego z firmy Netscape
Communications w 2001 roku, kiedy pojawia si przegldarka Netscape 6 (Mozilla 0.6).
Narzdzie wsppracuje ze wszystkimi wersjami Mozilli, ktre pojawiy si od tamtego
czasu oraz z innymi wspczenie uywanymi przegldarkami (IE, Opera, Safari).
Rozdzia 4.
Dziedziczenie
147
Przeznaczenie
Narzdzie xbObject ma z zaoenia udostpnia lepszy model obiektowy w jzyku JavaScript, umoliwiajcy nie tylko dziedziczenie, ale rwnie przeadowywanie metod oraz
moliwo wywoywanie metod nadklasy. W tym celu xbObject wymaga przejcia przez
kilka krokw.
Na pocztek musimy zarejestrowa klas i przy okazji zdefiniowa, ktrej klasy ma by
potomkiem. Wymaga to nastpujcego wywoania:
_classes.registerClass("Nazwa_Podklasy", "Nazwa_Nadklasy");
Nazwy podklasy i nadklasy s tu przesyane jako cigi znakowe, a nie jako wskaniki do swoich
konstruktorw. Wywoanie to musi nastpowa przed konstruktorem danej podklasy.
Mona te wywoa registerClass() z jednym tylko argumentem, jeeli nowa
klasa nie jest potomkiem adnej innej klasy.
Drugi krok to wywoanie w obrbie konstruktora metody defineClass(), z przesaniem nazwy klasy oraz wskanika na co, co Clary nazywa funkcj prototypow, suc do inicjalizacji wszystkich waciwoci i metod obiektu (o tym pniej). Na przykad:
_classes.registerClass("ClassA");
function ClassA(sColor) {
_classes.defineClass("ClassA", prototypeFunction);
function prototypeFunction() {
// ...
}
}
148
};
Widzimy tu metod parentMethod() wywoywan w metodzie init(). W ten wanie sposb xbObject umoliwia klasom wywoywanie metod nadklasy. Metoda parentMethod()
przyjmuje dowoln ilo argumentw, ale pierwszy argument jest zawsze nazw metody
klasy nadrzdnej, ktra ma by wywoana (argument ten musi by cigiem, a nie wskanikiem funkcji). Wszystkie nastpne argumenty zostan przesane do metody nadklasy.
W tym przypadku najpierw wywoywana jest metoda init() nadklasy, co jest wymagane dla
dziaania xbObject. Mimo e klasa ClassA nie rejestrowaa adnej nadklasy, xbObject tworzy
domyln nadklas dla wszystkich klas i stamtd wanie pochodzi metoda init() z nadklasy.
Czwarty i ostatni krok polega na dodaniu metod innej klasy w obrbie funkcji prototypowej:
classes.registerClass("ClassA");
function ClassA(sColor) {
_classes.defineClass("ClassA", prototypeFunction);
this.init(sColor);
function prototypeFunction() {
ClassA.prototype.init = function (sColor) {
this.parentMethod("init");
this.sColor = sColor;
};
ClassA.prototype.sayColor = function () {
alert(this.sColor);
};
}
Rozdzia 4.
Dziedziczenie
149
this.init(iSides);
function prototypeFunction() {
Polygon.prototype.init = function(iSides) {
this.parentMethod("init");
this.iSides = iSides;
};
Polygon.prototype.getArea = function () {
return 0;
};
}
Teraz przepisujemy klas Triangle i czujemy w tym przykadzie pierwszy smak prawdziwego dziedziczenia:
_classes.registerClass("Triangle", "Polygon");
function Triangle(iBase, iHeight) {
_classes.defineClass("Triangle", prototypeFunction);
this.init(iBase,iHeight);
function prototypeFunction() {
Triangle.prototype.init = function(iBase, iHeight) {
this.parentMethod("init", 3);
this.iBase = iBase;
this.iHeight = iHeight;
};
Triangle.prototype.getArea = function () {
return 0.5 * this.iBase * this.iHeight;
};
150
Podstawowa rnica midzy t klas a klas Triangle (poza innymi wywoaniami registerClass() i defineClass()) to wywoanie metody init() z nadklasy z argumentem 4. Poza
tym dodane zostay waciwoci iLength i iWidth i zastpiona zostaa metoda getArea().
Podsumowanie
W rozdziale tym zapoznae si z koncepcj dziedziczenia w jzyku ECMAScript (a tym
samym w JavaScripcie) korzystajcego z maskowania obiektw i wizania acuchowego
prototypw. Dowiedziae si te, e czne stosowanie tych metod jest optymalnym sposobem
na stworzenie struktury dziedzicznej klas.
Przedstawiono tu te dwie alternatywne metody uzyskiwania dziedziczenia: zInherit i xbObject.
Te darmowe biblioteki JavaScriptu dostpne do pobrania w internecie wprowadzaj nowe i inne
moliwoci tworzenia struktur dziedzicznych.
W ten sposb kocz omawianie ECMAScriptu rdzenia jzyka JavaScript. W nastpnych
rozdziaach, opierajc si na tym fundamencie, bdziesz poznawa bardziej specyficzne dla
sieci WWW aspekty tego jzyka.