Spis tre ci
Wst p ........................................................................................................................................9
1. Mocne strony ................................................................................................................11
Dlaczego JavaScript? 12
Analizuj c JavaScript 12
Prosta platforma testowa 14
2. Gramatyka ................................................................................................................... 15
Biale znaki 15
Nazwy 16
Liczby 17
a cuchy znakowe 18
Instrukcje 20
Wyra enia 24
Literaly 27
Funkcje 28
3. Obiekty .........................................................................................................................29
Literaly obiektowe 29
Pobieranie 30
Modyfikacja 30
Referencja 31
Prototyp 31
Refleksja 32
Wyliczanie 32
Usuwanie 33
Ograniczanie liczby zmiennych globalnych 33
4. Funkcje .........................................................................................................................35
Obiekty funkcji 35
Literal funkcji 36
Wywolanie 36
Argumenty 39
5
Powrót z funkcji 40
Wyj tki 40
Rozszerzanie typów 41
Rekurencja 42
Zasi g 43
Domkni cia 44
Wywolania zwrotne 47
Modul 47
Kaskadowe l czenie wywola 49
Funkcja curry 50
Spami tywanie 51
5. Dziedziczenie ...............................................................................................................53
Dziedziczenie pseudoklasyczne 53
Okre lenia obiektów 56
Dziedziczenie prototypowe 56
Dziedziczenie funkcyjne 58
Cz ci 61
6. Tablice ..........................................................................................................................63
Literaly tablicowe 63
Dlugo tablicy 64
Usuwanie elementów 65
Wyliczanie 65
Problem z rozpoznawaniem typu 65
Metody 66
Wymiary 67
7. Wyra enia regularne ...................................................................................................69
Przyklad 70
Tworzenie 74
Elementy 75
8. Metody ......................................................................................................................... 81
9. Styl ................................................................................................................................ 97
10. Najpi kniejsze cechy j zyka .......................................................................................101
6 | Spis tre ci
Dodatek A K opotliwe cechy j zyka .................................................................................... 105
Dodatek B Nietrafione cechy j zyka .....................................................................................113
Dodatek C JSLint ....................................................................................................................119
Dodatek D Diagramy sk adni ............................................................................................... 129
Dodatek E JSON .................................................................................................................... 139
Skorowidz ............................................................................................................................. 149
Spis tre ci | 7
ROZDZIA 5.
Dziedziczenie
Dziedziczenie jest wa nym zagadnieniem w wi kszo ci j zyków programowania.
W j zykach klasycznych (takich jak Java) dziedziczenie ma dwa glówne zadania. Po pierwsze,
jest form wielokrotnego u ycia kodu. Je li nowa klasa jest podobna do istniej cej, wystarczy
okre li dziel ce je ró nice. Ponowne wykorzystanie kodu jest wa ne, poniewa pozwala zmniej-
szy koszty wytwarzania oprogramowania. Drug zalet dziedziczenia jest to, e zawiera ono
w sobie specyfikacj systemu typów. Uwalnia to programistów w du ej mierze od konieczno ci
rzutowania z jednego typu na drugi, co jest istotn zalet , poniewa rzutowanie podwa a
cal warto bezpiecze stwa systemu typów.
JavaScript, jako j zyk bez kontroli typów, nigdy nie wymaga rzutowania. Hierarchia dziedzi-
czenia obiektu nie ma tu znaczenia. Wa ne jest, co obiekty potrafi robi , a nie po czym
dziedzicz .
JavaScript posiada du o bogatszy zasób mo liwo ci ponownego wykorzystania kodu. Mo e
na ladowa klasyczne wzorce, ale dostarcza równie innych, bardziej ekspresyjnych. Zbiór
mo liwych wzorców dziedziczenia w JavaScripcie jest bardzo szeroki. W tym rozdziale przyj-
rzymy si kilku najprostszym przypadkom. Du o bardziej skomplikowane s równie mo -
liwe, ale zazwyczaj lepiej jest trzyma si tych najprostszych.
W klasycznych j zykach obiekty s instancjami klas, a klasa mo e dziedziczy po innej klasie.
JavaScript jest j zykiem prototypowym, co oznacza, e obiekty dziedzicz bezpo rednio z in-
nych obiektów.
Dziedziczenie pseudoklasyczne
J zyk JavaScript ma wewn trznie gl boko rozdart natur . Jego mechanizm prototypowy jest
zaciemniany przez niektóre skomplikowane elementy skladni, które wygl daj bardziej kla-
sycznie. Zamiast pozwoli obiektom dziedziczy bezpo rednio z innych obiektów, JavaScript
wprowadza niepotrzebny poziom abstrakcji, w którym obiekty tworzone s przy u yciu
funkcji konstruktorów.
Kiedy tworzony jest obiekt funkcji, konstruktor Function zwracaj cy obiekt funkcji wykonuje
kod podobny do poni szego:
this.prototype = {constructor: this};
53
Nowy obiekt funkcji otrzymuje wla ciwo prototype, której warto ci jest obiekt posiadaj cy
wla ciwo constructor, której to z kolei warto ci jest nowy obiekt funkcji. Obiekt prototype
jest miejscem, gdzie zlo one maj by odziedziczone wla ciwo ci. Ka da funkcja otrzymuje
obiekt prototype, poniewa j zyk nie posiada sposobu okre lenia, które funkcje s przezna-
czone do u ycia w roli konstruktorów. Wla ciwo constructor nie jest zbyt u yteczna. To
obiekt prototype ma znaczenie.
Kiedy funkcja jest wywolywana wedlug wzorca wywolania konstruktora z u yciem slowa
new, zmienia si sposób wykonania funkcji. Gdyby operator new byl metod , a nie operatorem,
móglby by zaimplementowany w ten sposób:
Function.method('new', function () {
// Tworzymy nowy obiekt dziedzicz cy z prototypu konstruktora.
var that = Object.beget(this.prototype);
// Wywo ujemy konstruktor, wi c this do nowego obiektu.
var other = this.apply(that, arguments);
// Je li zwracana warto ć nie jest obiektem,
// podmie j na nowo utworzony obiekt.
return (typeof other === 'object' && other) || that;
});
Mo emy zdefiniowa konstruktor i rozszerzy jego prototyp:
var Mammal = function (name) {
this.name = name;
};
Mammal.prototype.get_name = function () {
return this.name;
};
Mammal.prototype.says = function () {
return this.saying || '';
};
Tworzymy instancj :
var myMammal = new Mammal('Mój ssak');
var name = myMammal.get_name(); // 'Mój ssak'
Nast pnie mo emy utworzy inn pseudoklas dziedzicz c z Mammal, definiuj c jej konstruktor
i zast puj c jej prototyp instancj Mammal:
var Cat = function (name) {
this.name = name;
this.saying = 'miau';
};
// Zast pujemy Cat.prototype instancj Mammal
Cat.prototype = new Mammal();
// Rozszerzamy nowy prototyp metodami purr i get_name
Cat.prototype.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
54 | Rozdzia 5. Dziedziczenie
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
Cat.prototype.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
var myCat = new Cat('Kicia');
var says = myCat.says(); // 'miau'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.get_name(); // 'miau Kicia miau'
Pseudoklasyczne dziedziczenie mialo w zamiarach wygl da bardziej obiektowo, lecz w prak-
tyce wygl da dziwnie i obco. Cz brzydoty tego rozwi zania mo emy ukry , u ywaj c metody
method i definiuj c metod inherits:
Function.method('inherits', function (Parent) {
this.prototype = new Parent();
return this;
});
Obie te metody zwracaj this, wi c mo emy je wykorzysta przy l czeniu wywola . Mo e-
my teraz utworzy nasz obiekt Cat za pomoc jednej instrukcji1:
var Cat = function (name) {
this.name = name;
this.saying = 'miau';
}.
inherits(Mammal).
method('purr', function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
}).
method('get_name', function () {
return this.says() + ' ' + this.name + ' ' + this.says();
});
Dzi ki ukryciu calego mechanizmu operuj cego na obiekcie prototype, ten kod wygl da
nieco mniej obco. Ale czy naprawd co ulepszyli my? Mamy teraz funkcje konstruktorów
przypominaj ce klasy, ale pod t powierzchni wci mo emy si natkn na nieprzewidziane
zachowanie. Nie mamy prywatnego dost pu wszystkie wla ciwo ci s publiczne. Nie mamy
mo liwo ci wywolywania nieprzesloni tych wersji metod obiektów nadrz dnych z poziomu
obiektów przeslaniaj cych te metody.
Co gorsza, istnieje pewne ryzyko zwi zane z u yciem funkcji konstruktorów. Je li zapomni-
my u y slowa new przy wywolywaniu konstruktora, this nie b dzie powi zane z nowym
1
Kod ten w celu poprawnego nadpisania metody get_name wymaga u ycia implementacji metody method
podanej na ko cu rozdzialu 1, a nie jej modyfikacji warunkowej pokazanej na ko cu podrozdzialu Rozsze-
rzanie typów w rozdziale 4. przyp. t um.
Dziedziczenie pseudoklasyczne | 55
obiektem, lecz z obiektem globalnym. Zamiast wi c doda wla ciwo ci do nowego obiektu, za-
miecimy nimi globaln przestrze nazw. To bardzo niebezpieczna mo liwo , tym bardziej,
e nie otrzymamy adnego ostrze enia ani podczas kompilacji, ani podczas wykonania kodu.
Jest to powa ny bl d projektowy j zyka. Aby sobie jako z nim radzi , istnieje konwencja na-
kazuj ca nadawanie wszystkim funkcjom pelni cym rol konstruktorów (i adnym innym)
nazw zaczynaj cych si od wielkich liter. To pozwala przynajmniej mie nadziej , e analiza
kodu pozwoli wylapa wszystkie brakuj ce zastosowania new. Lepsz alternatyw jest jednak
nie u ywanie w ogóle slowa new.
Pseudoklasyczna forma dziedziczenia pozwala poczu si wygodniej programistom niezna-
j cym dobrze JavaScriptu, ale równocze nie ukrywa ona prawdziw natur tego j zyka. No-
tacja inspirowana podej ciem klasycznym mo e zach ca do tworzenia przesadnie gl bokich
i skomplikowanych hierarchii. Wi kszo takich skomplikowanych hierarchii klas spowodo-
wana jest ograniczeniami statycznej kontroli typów. JavaScript nie ma takich ogranicze . W j -
zykach klasycznych dziedziczenie klas jest jedyn form wielokrotnego wykorzystania kodu.
JavaScript ma du o szersze mo liwo ci.
Okre lenia obiektów
Czasami zdarza si , e konstruktor pobiera bardzo du liczb parametrów. Mo e to by
klopotliwe, bo na przyklad utrudnia zapami tanie kolejno argumentów. W takich wypad-
kach lepszym rozwi zaniem mo e by napisanie konstruktora, który pobiera pojedyncze
okre lenie obiektu (ang. object specifier). Okre lenie takie zawiera specyfikacj obiektu, który
ma by skonstruowany. Tak wi c, zamiast takiego wywolania funkcji:
var myObject = maker(f, l, m, c, s);
mo emy napisa :
var myObject = maker({
first: f,
last: l,
state: s,
city: c
});
Argumenty mog by teraz wymienione w dowolnej kolejno ci, mog by pomini te, je li
konstruktor jest w stanie przypisa im warto ci domy lne, a caly kod jest latwiejszy w czytaniu.
Podej cie to mo e mie dodatkowe zalety przy korzystaniu z formatu JSON (patrz dodatek E).
Tekst formatu JSON jest w stanie opisa jedynie dane, ale czasami dane reprezentuj jaki
obiekt i wygodnie byloby powi za dane z jego metodami. Okazuje si to banalne, gdy kon-
struktor pobiera okre lenie obiektu, poniewa mo emy po prostu przesla obiekt JSON do
konstruktora, który z kolei zwróci w pelni utworzony obiekt.
Dziedziczenie prototypowe
W wypadku podej cia czysto prototypowego, nie u ywamy w ogóle klas. Skupiamy si wy-
l cznie na obiektach. Dziedziczenie prototypowe jest koncepcyjnie prostsze od klasycznego:
nowy obiekt mo e dziedziczy wla ciwo ci starego obiektu. Jest to mo e mniej powszechne
podej cie, ale jest za to bardzo latwe do zrozumienia. Punktem wyj cia jest utworzenie jakiego
56 | Rozdzia 5. Dziedziczenie
po ytecznego obiektu. W nast pnym kroku mo emy utworzy wiele obiektów podobnych
do niego. Proces klasyfikacji polegaj cy na rozbiciu aplikacji na zbiór zagnie d onych klas
abstrakcyjnych mo e by calkowicie pomini ty.
Zacznijmy wi c od utworzenia takiego po ytecznego obiektu, u ywaj c literalu obiektowego:
var myMammal = {
name : "Mój ssak",
get_name : function () {
return this.name;
},
says : function () {
return this.saying || '';
}
};
Maj c obiekt taki jak ten, mo emy utworzy wi cej instancji korzystaj c z funkcji Ob-
ject.beget z rozdzialu 3. Nast pnie mo emy je dostosowa do naszych potrzeb:
var myCat = Object.beget(myMammal);
myCat.name = 'Kicia';
myCat.saying = 'miau';
myCat.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
myCat.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
Jest to dziedziczenie ró nicowe. Zdefiniowanie nowego obiektu polega na okre leniu ró nic
mi dzy nim a obiektem, z którego zostal utworzony.
Czasami wygodnie jest, gdy struktura danych dziedziczy z innej struktury. Oto przyklad:
przypu my, e analizujemy kod j zyka takiego jak Java lub TEX, w którym para nawiasów
oznacza zasi g. Zmienne zdefiniowane w ramach zasi gu nie s widoczne poza nim. W pewnym
sensie wewn trzny zasi g dziedziczy po zewn trznym. Obiekty JavaScriptu dobrze si na-
daj do reprezentacji takiej zale no ci. Funkcja block wywolywana jest, kiedy napotkany zo-
staje lewy nawias klamrowy. Funkcja parse b dzie pobiera symbole z zasi gu oraz doda-
wa nowo napotkane:
var block = function () {
// Zapami tujemy dotychczasowy zasi g. Tworzymy nowy zasi g,
// który b dzie zawiera wszystko to, co dotychczasowy.
var oldScope = scope;
scope = Object.beget(scope);
// Przechodz c nad lewym nawiasem klamrowym wchodzimy do nowego zasi gu.
advance('{');
// Analizujemy tekst, u ywaj c nowego zasi gu.
parse(scope);
Dziedziczenie prototypowe | 57
// Przechodz c nad prawym nawiasem klamrowym wychodzimy z zasi gu
// i przywracamy stary zasi g.
advance('}');
scope = oldScope;
};
Dziedziczenie funkcyjne
Przedstawione dot d wzorce dziedziczenia maj pewien slaby punkt: brak prywatno ci. Wszyst-
kie wla ciwo ci obiektów s widoczne. Brakuje zmiennych prywatnych oraz prywatnych me-
tod. Czasami nie ma to wi kszego znaczenia, ale czasami mo e mie znaczenie ogromne.
Zniech ceni tym faktem niektórzy niedoinformowani programi ci ukuli zasad u ywania
zmiennych niby-prywatnych. Maj c wla ciwo , któr chcieli uczyni prywatn , nadawali jej
dziwnie wygl daj c nazw , maj c nadziej , e inni u ytkownicy ich kodu b d udawa , e
nie widz nazw dziwnie wygl daj cych. Na szcz cie istnieje du o lepsza alternatywa oparta
o omawiany wcze niej wzorzec modulu.
Zaczynamy od napisania funkcji, która b dzie wytwarza obiekty. Dajemy jej nazw zaczy-
naj c si od malej litery, poniewa nie b dzie ona wymaga u ycia slowa new. Dzialanie
funkcji sklada si z czterech kroków:
" Funkcja tworzy nowy obiekt. Jest na to wiele sposobów: u ycie literalu obiektowego,
wywolanie funkcji konstruktora ze slowem new, u ycie metody Object.beget do utwo-
rzenia nowego obiektu na podstawie istniej cego, wreszcie wywolanie dowolnej innej
funkcji zwracaj cej obiekt.
" Opcjonalnie funkcja deklaruje zmienne i metody prywatne. S to zwykle zmienne (var)
funkcji.
" Nast pnie funkcja rozszerza nowo utworzony obiekt o metody. Metody te maj uprzy-
wilejowany dost p do parametrów i zmiennych zdefiniowanych w poprzednim kroku.
" Na koniec nowy obiekt jest zwracany.
Oto szablon pseudokodu do tworzenia konstruktora funkcyjnego (pogrubiony tekst dodano
dla podkre lenia niektórych fragmentów):
var constructor = function (spec, my) {
vat that, inne prywatne zmienne instancyjne;
my = my || {};
// tu dodajemy do obiektu my zmienne i funkcje wspó dzielone
that = nowy obiekt;
// tu dodajemy do obiektu that metody uprzywilejowane
return that;
}
Obiekt spec zawiera wszystkie informacje niezb dne konstruktorowi do utworzenia instancji.
Zawarto obiektu spec mo e by skopiowana do zmiennych prywatnych lub przetworzona
przez inne funkcje, b d te metody mog pobiera informacje z tego obiektu w razie potrzeby.
(Mo na te to upro ci zast puj c spec pojedyncz warto ci . Jest to wygodne, gdy tworzony
obiekt nie wymaga pelnego obiektu spec.)
58 | Rozdzia 5. Dziedziczenie
Obiekt my slu y przechowywaniu chronionych danych, które mog by wspóldzielone z kon-
struktorami w ramach la cucha dziedziczenia. U ycie tego obiektu jest opcjonalne. Je li nie jest
on przekazany do funkcji, tworzony jest pusty.
Nast pnie deklarujemy prywatne zmienne instancyjne oraz prywatne metody obiektu. Odby-
wa si to poprzez zwykle zadeklarowanie zmiennych. Zmienne i funkcje wewn trzne utwo-
rzone wewn trz konstruktora staj si zmiennymi i funkcjami prywatnymi instancji. Funkcje
wewn trzne maj dost p do spec, my, that i innych zmiennych prywatnych.
Nast pnie dodajemy wspóldzielone dane chronione do obiektu my. Wykonuje si to poprzez
przypisanie:
my.member = value;
Teraz tworzymy nowy obiekt i przypisujemy go do that. Jest wiele sposobów utworzenia
obiektu. Mo emy u y literalu obiektowego. Mo emy wywola pseudoklasyczny konstruktor,
u ywaj c operatora new. Mo emy wywola metod Object.beget na prototypie obiektu.
Wreszcie mo emy skorzysta z dowolnego innego konstruktora funkcyjnego, przekazuj c mu
obiekt spec (najcz ciej ten sam, który zostal przekazany do tego konstruktora) oraz obiekt my.
Obiekt my pozwala na wspóldzielenie informacji z innymi konstruktorami. Inne konstruktory
mog równie umieszcza swoje wlasne chronione dane w tym obiekcie, w celu wspóldzie-
lenia ich z naszych konstruktorem.
Nast pnie rozszerzamy that, dodaj c metody uprzywilejowane stanowi ce interfejs obiektu.
Mo emy przypisywa nowe funkcje bezpo rednio do that. Lub te , w bardziej bezpieczny spo-
sób, najpierw zdefiniowa funkcje jako metody prywatne, a nast pnie przypisa je do that:
var methodical = function () {
...
};
that.methodical = methodical;
Zalet definiowania metod w dwóch etapach jest to, e je li inne metody b d chcialy wywo-
lywa methodical, b d mogly zrobi to przez wywolanie methodical() zamiast that.metho
dical(). Je li instancja zostanie uszkodzona lub zmanipulowana, tak e metoda that.
methodical zostanie zast piona inn , wówczas funkcje wywoluj ce funkcj methodical
b d nadal dziala poprawnie, poniewa funkcja prywatna methodical pozostanie nienaru-
szona mimo modyfikacji obiektu.
Na koniec zwracamy that.
Zastosujmy ten wzorzec do naszego przykladu z ssakami. Nie potrzebujemy tutaj zmiennej my,
wi c po prostu j opu cimy, ale za to przyda nam si obiekt spec.
Wla ciwo ci name i saying s teraz calkowicie prywatne. Dost p do nich jest mo liwy tylko
dzi ki uprzywilejowanym metodom get_name i says:
var mammal = function (spec) {
var that = {};
that.get_name = function () {
return spec.name;
};
that.says = function () {
return spec.saying || '';
};
Dziedziczenie funkcyjne | 59
return that;
};
var myMammal = mammal({name: 'Mój ssak'});
W podej ciu pseudoklasycznym konstruktor Cat musial duplikowa prac wykonywan przez
konstruktor Mammal. We wzorcu funkcyjnym nie jest to konieczne, poniewa konstruktor Cat
wywola konstruktor Mammal, który sam wykona swoje zadanie. Konstruktor Cat zajmuje si
tylko ró nicami mi dzy nimi:
var cat = function (spec) {
spec.saying = spec.saying || 'miau';
var that = mammal(spec);
that.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
that.get_name = function () {
return that.says() + ' ' + spec.name + ' ' + that.says();
};
return that;
};
var myCat = cat({name: 'Kicia'});
Wzorzec funkcyjny umo liwia nam równie wywolywanie metod z obiektów nadrz dnych.
Napiszmy metod superior, która pobiera b dzie nazw metody i zwraca funkcj wywo-
luj c t metod . Funkcja ta b dzie wywolywa oryginaln metod , nawet gdy wla ciwo
zostala zmieniona przez obiekt potomny:
Object.method('superior', function (name) {
var that = this,
method = that[name];
return function () {
return method.apply(that, arguments);
};
});
Wypróbujmy j na obiekcie coolcat, który jest podobny do obiektu cat, ale ma nieco ciekaw-
sz metod get_name, która wywoluje nieprzesloni t wersj metody z obiektu nadrz dnego.
Wymaga to tylko niewielkich przygotowa . Zadeklarujemy zmienn super_get_name i przypi-
szemy jej wynik wywolania metody superior:
var coolcat = function (spec) {
var that = cat(spec),
super_get_name = that.superior('get_name');
that.get_name = function (n) {
return 'Teraz ' + super_get_name() + ' w nowej, lepszej wersji';
};
return that;
};
var myCoolCat = coolcat({name: 'Kocur'});
var name = myCoolCat.get_name(); // 'Teraz miau Kocur miau w nowej, lepszej wersji'
60 | Rozdzia 5. Dziedziczenie
Czytaj dalej...
Wyszukiwarka
Podobne podstrony:
JavaScript mocne strony jscmocinformatyka ajax wzorce projektowe michael mahemoff ebookinformatyka ajax on rails scott raymond ebookinformatyka wordpress 3 instalacja i zarzadzanie lukasz wojcik ebookinformatyka laptopy dla seniorow bartosz danowski ebookinformatyka marketing wirusowy w internecie piotr r michalak ebookinformatyka wordpress praktyczne projekty witold wrotek ebookinformatyka jquery kod doskonaly pawel mikolajewski ebookKurs JavaScript Inne strony o JavaScriptinformatyka jquery poradnik programisty wlodzimierz gajda ebookinformatyka asembler leksykon kieszonkowy dawid farbaniec ebookinformatyka 3ds max leksykon wojciech pazdur ebookinformatyka ruby leksykon kieszonkowy michael fitzgerald ebookwięcej podobnych podstron