JavaScript.
Programowanie obiektowe
Autor: Stoyan Stefanov
T³umaczenie: Justyna Walkowska
ISBN: 978-83-246-2242-9
Tytu³ orygina³u:
Format: B5, stron: 336
Poznaj obiektowe mo¿liwoœci JavaScript!
• Jak rozpocz¹æ przygodê z jêzykiem JavaScript?
• Jak rozszerzaæ obiekty wbudowane?
• Jak pracowaæ w œrodowisku przegl¹darki?
JavaScript jest obiektowym, skryptowym jêzykiem programowania. Choæ sw¹ b³yskotliw¹
karierê jêzyk ten rozpocz¹³ ponad dwanaœcie lat temu, swoimi mo¿liwoœciami wci¹¿
potrafi zaskoczyæ nawet doœwiadczonego programistê. Ostatnio – dziêki technologii
AJAX – znów osi¹gn¹³ on sw¹ szczytow¹ formê. Wykorzystuj¹c w odpowiedni sposób
jego w³aœciwoœci, sprawisz, ¿e twój serwis WWW stanie siê bardziej interaktywny
i dynamiczny.
Dziêki tej ksi¹¿ce dowiesz siê, w jaki sposób u¿yæ do swoich celów obiektowych
mo¿liwoœci jêzyka JavaScript. Jednak zanim zapoznasz siê z tymi tematami, autor
w niezwykle przejrzysty sposób przedstawi Ci podstawy tego jêzyka. Zobaczysz, w jaki
sposób dzia³aj¹ funkcje, pêtle oraz model DOM. Ponadto nauczysz siê korzystaæ ze
wzorców projektowych, wyra¿eñ regularnych oraz prototypów. Pomimo zaawansowanej
tematyki poruszanej przez autora tej ksi¹¿ki dziêki przejrzystemu jêzykowi i klarownemu
uk³adowi stanowi ona œwietn¹ lekturê równie¿ dla pocz¹tkuj¹cych programistów.
• Pojêcia zwi¹zane z programowaniem obiektowym
• Typy danych, tablice, pêtle, sterowanie wykonaniem
• Wykorzystanie funkcji
• Domkniêcia
• Obiekty wbudowane
• Zastosowanie konstruktorów
• Tablice asocjacyjne
• U¿ycie prototypów
• Rozszerzanie obiektów wbudowanych
• Dziedziczenie
• Praca w œrodowisku przegl¹darki (modele BOM i DOM)
• Wzorce kodowania i wzorce projektowe
Od podstaw do sprawnego programowania obiektowego!
Spis treci
O autorze
13
O recenzentach
15
Przedmowa
19
Co znajdziesz w tej ksice?
19
Konwencje
20
Rozdzia 1. Wprowadzenie
23
Troch historii
24
Zapowied zmian
25
Teraniejszo
26
Przyszo
26
Programowanie obiektowe
27
Obiekty
27
Klasy
28
Kapsukowanie
28
Agregacja
29
Dziedziczenie
29
Polimorfizm
30
Programowanie obiektowe — podsumowanie
30
Konfiguracja rodowiska rozwijania aplikacji
31
Niezbdne narzdzia
31
Korzystanie z konsoli Firebug
32
Podsumowanie
33
Rozdzia 2. Proste typy danych, tablice, ptle i warunki
35
Zmienne
35
Wielko liter ma znaczenie
36
Operatory
37
Spis
treci
6
Proste typy danych
40
Ustalanie typu danych — operator typeof
41
Liczby
41
Liczby ósemkowe i szesnastkowe
41
Wykadniki potg
42
Nieskoczono
43
NaN
45
acuchy znaków
45
Konwersje acuchów
46
Znaki specjalne
47
Typ boolean
48
Operatory logiczne
49
Priorytety operatorów
51
Leniwe wartociowanie
52
Porównywanie
53
Undefined i null
54
Proste typy danych — podsumowanie
56
Tablice
56
Dodawanie i aktualizacja elementów tablicy
57
Usuwanie elementów
58
Tablice tablic
58
Warunki i ptle
60
Bloki kodu
60
Warunki if
61
Sprawdzanie, czy zmienna istnieje
62
Alternatywna skadnia if
63
Switch
63
Ptle
65
Ptla while
66
Ptla do…while
66
Ptla for
66
Ptla for…in
69
Komentarze
70
Podsumowanie
71
wiczenia
71
Rozdzia 3. Funkcje
73
Czym jest funkcja?
74
Wywoywanie funkcji
74
Parametry
74
Funkcje predefiniowane
76
parseInt()
76
parseFloat()
78
isNaN()
79
isFinite()
79
Encode/Decode URIs
80
eval()
80
Bonus — funkcja alert()
81
Spis treci
7
Zasig zmiennych
81
Funkcje s danymi
83
Funkcje anonimowe
84
Wywoania zwrotne
84
Przykady wywoa zwrotnych
85
Funkcje samowywoujce si
87
Funkcje wewntrzne (prywatne)
87
Funkcje, które zwracaj funkcje
88
Funkcjo, przepisz e si!
89
Domknicia
90
acuch zakresów
91
Zasig leksykalny
91
Przerwanie acucha za pomoc domknicia
93
Domknicie 1.
94
Domknicie 2.
95
Domknicie 3. i jedna definicja
96
Domknicia w ptli
96
Funkcje dostpowe
98
Iterator
99
Podsumowanie
100
wiczenia
100
Rozdzia 4. Obiekty
103
Od tablic do obiektów
103
Elementy, pola, metody
105
Tablice asocjacyjne
105
Dostp do wasnoci obiektu
106
Wywoywanie metod obiektu
107
Modyfikacja pól i metod
108
Warto this
109
Konstruktory
109
Obiekt globalny
110
Pole constructor
112
Operator instanceof
112
Funkcje zwracajce obiekty
113
Przekazywanie obiektów
114
Porównywanie obiektów
114
Obiekty w konsoli Firebug
115
Obiekty wbudowane
117
Object
117
Array
118
Ciekawe metody obiektu Array
120
Function
122
Wasnoci obiektu Function
123
Metody obiektu Function
125
Nowe spojrzenie na obiekt arguments
126
Boolean
127
Number
128
Spis
treci
8
String
130
Ciekawe metody obiektu String
132
Math
135
Date
136
Metody dziaajce na obiektach Date
138
RegExp
140
Pola obiektów RegExp
141
Metody obiektów RegExp
142
Metody obiektu String, których parametrami mog by wyra enia regularne
143
search() i match()
143
replace()
144
Wywoania zwrotne replace
145
split()
146
Przekazanie zwykego tekstu zamiast wyra enia regularnego
146
Obsuga bdów za pomoc obiektów Error
146
Podsumowanie
150
wiczenia
151
Rozdzia 5. Prototypy
155
Pole prototype
155
Dodawanie pól i metod przy u yciu prototypu
156
Korzystanie z pól i metod obiektu prototype
157
Wasne pola obiektu a pola prototypu
158
Nadpisywanie pól prototypu wasnymi polami obiektu
159
Pobieranie listy pól
160
isPrototypeOf()
162
Ukryte powizanie __proto__
163
Rozszerzanie obiektów wbudowanych
165
Rozszerzanie obiektów wbudowanych — kontrowersje
166
Puapki zwizane z prototypami
167
Podsumowanie
169
wiczenia
170
Rozdzia 6. Dziedziczenie
171
acuchy prototypów
172
Przykadowy acuch prototypów
172
Przenoszenie wspólnych pól do prototypu
175
Dziedziczenie samego prototypu
177
Konstruktor tymczasowy — new F()
178
Uber: dostp do obiektu-rodzica
180
Zamknicie dziedziczenia wewntrz funkcji
181
Kopiowanie pól
182
Uwaga na kopiowanie przez referencj!
184
Obiekty dziedzicz z obiektów
186
Gbokie kopiowanie
187
object()
189
Poczenie dziedziczenia prototypowego z kopiowaniem pól
190
Spis treci
9
Dziedziczenie wielokrotne
191
Miksiny
193
Dziedziczenie pasoytnicze
193
Wypoyczanie konstruktora
194
Po ycz konstruktor i skopiuj jego prototyp
196
Podsumowanie
197
Studium przypadku: rysujemy ksztaty
200
Analiza
200
Implementacja
201
Testowanie
204
wiczenia
205
Rozdzia 7. rodowisko przegldarki
207
czenie JavaScriptu z kodem HTML
207
BOM i DOM — przegld
208
BOM
209
Ponownie odkrywamy obiekt window
209
window.navigator
210
Firebug jako ciga
210
window.location
211
window.history
212
window.frames
213
window.screen
214
window.open() i window.close()
215
window.moveTo(), window.resizeTo()
216
window.alert(), window.prompt(), window.confirm()
216
window.setTimeout(), window.setInterval()
217
window.document
219
DOM
219
Core DOM i HTML DOM
221
Dostp do wzów DOM
222
Wze document
223
documentElement
224
Wzy-dzieci
224
Atrybuty
225
Dostp do zawartoci znacznika
226
Uproszczone metody dostpowe DOM
227
Rówienicy, body, pierwsze i ostatnie dziecko
228
Spacer przez wzy DOM
230
Modyfikacja wzów DOM
230
Modyfikacja stylu
231
Zabawa formularzami
232
Tworzenie nowych wzów
233
Metoda w peni zgodna z DOM
234
cloneNode()
235
insertBefore()
236
Usuwanie wzów
236
Spis
treci
10
Obiekty DOM istniejce tylko w HTML
238
Starsze sposoby dostpu do dokumentu
239
document.write()
240
Pola cookies, title, referrer i domain
240
Zdarzenia
242
Kod obsugi zdarze wpleciony w atrybuty HTML
242
Pola elementów
242
Obserwatorzy zdarze DOM
243
Przechwytywanie i bbelkowanie
244
Zatrzymanie propagacji
246
Anulowanie zachowania domylnego
248
Obsuga zdarze w ró nych przegldarkach
248
Typy zdarze
249
XMLHttpRequest
250
Wysanie dania
251
Przetworzenie odpowiedzi
252
Tworzenie obiektów XHR w IE w wersjach starszych ni 7
253
A jak asynchroniczny
254
X jak XML
254
Przykad
254
Podsumowanie
257
wiczenia
258
Rozdzia 8. Wzorce kodowania i wzorce projektowe
261
Wzorce kodowania
262
Izolowanie zachowania
262
Warstwa treci
262
Warstwa prezentacji
263
Zachowanie
263
Przykad wydzielenia warstwy zachowania
263
Przestrzenie nazw
264
Obiekt w roli przestrzeni nazw
264
Konstruktory w przestrzeniach nazw
265
Metoda namespace()
266
Rozgazianie kodu w czasie inicjalizacji
267
Leniwe definicje
268
Obiekt konfiguracyjny
269
Prywatne pola i metody
270
Metody uprzywilejowane
271
Funkcje prywatne w roli metod publicznych
272
Funkcje samowywoujce si
273
acuchowanie
273
JSON
274
Wzorce projektowe
275
Singleton
276
Singleton 2
276
Zmienna globalna
277
Pole konstruktora
277
Pole prywatne
278
Spis treci
11
Fabryka
278
Dekorator
280
Dekorowanie choinki
280
Obserwator
282
Podsumowanie
285
Dodatek A Sowa zarezerwowane
287
Lista sów zarezerwowanych majcych specjalne znaczenie w jzyku JavaScript
287
Lista sów zarezerwowanych na uytek przyszych implementacji
288
Dodatek B Funkcje wbudowane
291
Dodatek C Obiekty wbudowane
295
Object
295
Skadowe konstruktora Object
296
Skadowe obiektów tworzonych przez konstruktor Object
296
Array
298
Skadowe obiektów Array
298
Function
301
Skadowe obiektów Function
301
Boolean
302
Number
302
Skadowe konstruktora Number
303
Skadowe obiektów Number
304
String
304
Skadowe konstruktora String
305
Skadowe obiektów String
305
Date
308
Skadowe konstruktora Date
308
Skadowe obiektów Date
309
Math
311
Skadowe obiektu Math
312
RegExp
313
Skadowe obiektów RegExp
314
Obiekty Error
315
Skadowe obiektów Error
315
Dodatek D Wyraenia regularne
317
Skorowidz
323
3
Funkcje
Opanowanie funkcji ma kluczowe znaczenie podczas nauki kadego jzyka programowania,
a w przypadku JavaScriptu jest jeszcze waniejsze ni zwykle. Jest tak dlatego, e w tym j-
zyku funkcje maj bardzo wiele zastosowa i w duej mierze to dziki nim JavaScript jest tak
elastyczny i ekspresywny. W miejscach, gdzie w innych jzykach programowania trzeba by
byo stosowa specjaln skadni w celu wykorzystania obiektowoci, JavaScript udostpnia
funkcje. Ten rozdzia omawia:
Q
definiowanie funkcji i korzystanie z nich,
Q
przekazywanie funkcjom parametrów,
Q
funkcje predefiniowane dostpne za darmo,
Q
zasig zmiennych,
Q
podejcie, zgodnie z którym funkcje to tylko dane specjalnego typu.
Zrozumienie powyszych tematów da nam solidne oparcie przed przejciem do kolejnej cz-
ci rozdziau, w której przedstawione zostan pewne ciekawe zastosowania funkcji:
Q
funkcje anonimowe;
Q
wywoania zwrotne;
Q
samowywoujce si funkcje;
Q
funkcje wewntrzne (zdefiniowane wewntrz innych funkcji);
Q
funkcje, które zwracaj inne funkcje;
Q
funkcje, które zmieniaj swoj definicj;
Q
domknicia.
JavaScript. Programowanie obiektowe
74
Czym jest funkcja?
Funkcje pozwalaj zgrupowa pewn ilo kodu, nada jej nazw, a nastpnie ponownie wy-
korzysta przy uyciu tej wanie nazwy. Spójrzmy na przykad:
function sum(a, b) {
var c = a + b;
return c;
}
Z jakich czci skada si funkcja?
Q
Sowo kluczowe
function
.
Q
Nazwa funkcji, w przykadzie jest to
sum
.
Q
Oczekiwane parametry (argumenty), w tym wypadku
a
i
b
. Funkcja moe mie ich
zero lub wicej. Jeli jest ich wicej ni jeden, parametry rozdziela si przecinkami.
Q
Blok kodu, nazywany ciaem funkcji.
Q
Instrukcja
return
, która umoliwia zwrócenie obliczonej wartoci funkcji. Funkcja
zawsze zwraca warto. Jeli nie robi tego w sposób jawny, niejawnie zwraca
warto
undefined
.
Zwró uwag, e funkcja moe zwróci tylko jedn warto. Jeli potrzebne jest zwrócenie
wikszej liczby wartoci, naley umieci je w tablicy i zwróci tablic jako warto funkcji.
Wywoywanie funkcji
Aby skorzysta z funkcji, naley j wywoa. Funkcj wywouje si poprzez podanie jej nazwy
i argumentów umieszczonych w nawiasie.
Wywoajmy zatem funkcj
sum()
, przekazujc jej dwa argumenty i przypisujc zwracan
przez ni warto zmiennej
result
.
>>> var result = sum(1, 2);
>>> result;
3
Parametry
Podczas definiowania funkcji mona okreli oczekiwane parametry. Funkcja nie musi pobie-
ra parametrów, ale jeli oczekuje, e je otrzyma, a programista podczas wywoywania funkcji
zapomni o ich podaniu, JavaScript przypisze im warto
undefined
. W poniszym przykadzie
funkcja zwraca warto
NaN
, poniewa próbuje doda
1
do
undefined
:
Rozdzia 3. • Funkcje
75
>>> sum(1)
NaN
JavaScript nie wybrzydza podczas pobierania parametrów. Jeli otrzyma ich wicej, ni jest
potrzebne, dodatkowe parametry zostan zignorowane:
>>> sum(1, 2, 3, 4, 5)
3
Na dodatek moliwe jest pisanie funkcji, które mog przyjmowa rón liczb parametrów. Jest to
moliwe dziki tablicy
arguments
, która jest automatycznie tworzona wewntrz kadej funkcji.
Oto funkcja, której dziaanie polega na zwracaniu wszystkich przekazanych jej argumentów:
>>> function args() { return arguments; }
>>> args();
[]
>>> args( 1, 2, 3, 4, true, 'ninja');
[1, 2, 3, 4, true, "ninja"]
Tablica
arguments
pozwoli nam poprawi funkcj
sum()
tak, by przyjmowaa ona dowoln
liczb parametrów i dodawaa je wszystkie.
function sumaNaSterydach() {
var i, res = 0;
var liczba_parametrow = arguments.length;
for (i = 0; i < liczba_parametrow; i++) {
res += arguments[i];
}
return res;
}
Jeli podczas testowania wywoasz t funkcj z inn ni wczeniej liczb parametrów (lub nawet
bez parametrów), zobaczysz, e dziaa tak, jak powinna:
>>> sumaNaSterydach(1, 1, 1);
3
>>> sumaNaSterydach(1, 2, 3, 4);
10
>>> sumaNaSterydach(1, 2, 3, 4, 4, 3, 2, 1);
20
>>> sumaNaSterydach(5);
5
JavaScript. Programowanie obiektowe
76
>>> sumaNaSterydach();
0
Wyraenie
arguments.length
zwraca liczb parametrów podanych podczas wywoania funkcji.
Jeli nie rozumiesz jego skadni, nie przejmuj si, wrócimy do tego w nastpnym rozdziale.
Wtedy take dowiesz si, e
arguments
w rzeczywistoci nie jest tablic, ale obiektem tablico-
podobnym.
Funkcje predefiniowane
Istnieje pewna liczba funkcji, które zostay wbudowane w silnik JavaScriptu i z których mo-
na korzysta do woli. Przyjrzyjmy si im. Warto poeksperymentowa z tymi funkcjami i przyj-
rze si ich argumentom i wartociom zwracanym, by móc pó niej korzysta z nich w wygodny
sposób. Oto lista funkcji wbudowanych:
Q
parseInt()
Q
parseFloat()
Q
isNaN()
Q
isFinite()
Q
encodeURI()
Q
decodeURI()
Q
encodeURIComponent()
Q
decodeURIComponent()
Q
eval()
Zasada czarnej skrzynki
Z reguy podczas korzystania z funkcji Twój program nie musi wiedzie, jakie czynnoci s wykonywane
wewntrz danej funkcji. Moesz myle o funkcjach jako o czarnych skrzynkach — podajesz im pewne
wartoci (w postaci parametrów wejciowych) i odbierasz od nich zwracane wyniki. Jest to prawdziwe
dla wszystkich funkcji — tych wbudowanych w jzyk JavaScript, tych pisanych przez Ciebie oraz tych
stworzonych przez Twoich wspópracowników lub nieznanych Ci programistów.
parseInt()
parseInt()
pobiera argument dowolnego typu (najczciej acuch znaków) i próbuje zamie-
ni go na liczb cakowit. Jeli operacja si nie powiedzie, zwrócona zostanie warto
NaN
.
>>> parseInt('123')
123
Rozdzia 3. • Funkcje
77
>>> parseInt('abc123')
NaN
>>> parseInt('1abc23')
1
>>> parseInt('123abc')
123
Funkcja pobiera jeszcze opcjonalny drugi argument, który okrela podstaw, opisujc typ
liczby: dziesitny, szesnastkowy, binarny itp. Przykadowo: nie ma sensu próba zamiany po-
brania liczby dziesitnej z acucha
"FF"
, zatem wynikiem bdzie NaN, jednak jeli potrak-
tujemy
"FF"
jako liczb szesnastkow, otrzymamy wynik 255.
>>> parseInt('FF', 10)
NaN
>>> parseInt('FF', 16)
255
Spróbujmy teraz sparsowa liczby o rónych podstawach:
10
(liczba dziesitna) i
8
(liczba
ósemkowa).
>>> parseInt('0377', 10)
377
>>> parseInt('0377', 8)
255
Jeli drugi argument nie zostanie podany, za podstaw uznawana jest liczba
10
, z nastpuj-
cymi wyjtkami:
Q
Jeli jako pierwszy argument przekazany zostanie acuch zaczynajcy si od
0x
,
drugiemu argumentowi (jeli nie zosta podany) przypisana zostanie warto
16
(liczba zostanie uznana za szesnastkow).
Q
Jeli pierwszy parametr zaczyna si od
0
, drugi otrzyma warto
8
.
>>> parseInt('377')
377
>>> parseInt('0377')
255
>>> parseInt('0x377')
887
JavaScript. Programowanie obiektowe
78
Najbezpieczniejszym rozwizaniem jest okrelanie podstawy za kadym razem. Jeli tego nie
zrobisz, kod prawdopodobnie zadziaa w 99% przypadków (poniewa najczciej parsuje si
liczby dziesitne), jednak jeli trafisz na liczb zapisan w innym systemie, moesz osiwie,
zanim uda Ci si znale przyczyn bdu. Wyobra sobie na przykad, e parsujesz pola for-
mularza, który reprezentuje kalendarz, i e uytkownik wpisa
08
, majc na myli sierpie.
Jeli nie podasz podstawy, otrzymasz wynik inny ni oczekiwany.
parseFloat()
parseFloat()
dziaa podobnie do
parseInt(),
ale oczekuje uamków. Pobiera ona tylko jeden
parametr.
>>> parseFloat('123')
123
>>> parseFloat('1.23')
1.23
>>> parseFloat('1.23abc.00')
1.23
>>> parseFloat('a.bc1.23')
NaN
Podobnie jak
parseInt()
,
parseFloat()
podda si po napotkaniu pierwszego znaku, z którym
nie bdzie umiaa sobie poradzi, nawet jeli pozostaa cz tekstu zawiera poprawne liczby.
>>> parseFloat('a123.34')
NaN
>>> parseFloat('a123.34')
NaN
>>> parseFloat('12a3.34')
12
parseFloat()
, w przeciwiestwie do
parseInt()
, jest w stanie poprawnie zinterpretowa zapis
wykadniczy.
>>> parseFloat('123e-2')
1.23
>>> parseFloat('123e2')
12300
Rozdzia 3. • Funkcje
79
>>> parseInt('1e10')
1
isNaN()
Przy pomocy
isNaN()
mona sprawdzi, czy warto wejciowa jest liczb, której mona bez-
piecznie uywa w operacjach arytmetycznych.
isNaN()
pozwala w wygodny sposób dowie-
dzie si, czy funkcjom
parseInt()
i
parseFloat()
udao si sparsowa liczb.
>>> isNaN(NaN)
true
>>> isNaN(123)
false
>>> isNaN(1.23)
false
>>> isNaN(parseInt('abc123'))
true
Ta funkcja take stara si zamieni parametr wejciowy na liczb:
>>> isNaN('1.23')
false
>>> isNaN('a1.23')
true
Funkcja
isNaN()
jest potrzebna take dlatego, e liczba
NaN
nie jest równa samej sobie. Wyni-
kiem porównania
NaN === NaN
bdzie false!
isFinite()
Funkcja
isFinite()
sprawdza, czy warto parametru wejciowego to liczba róna od
Infinity
i róna od
NaN
.
>>> isFinite(Infinity)
false
>>> isFinite(-Infinity)
false
JavaScript. Programowanie obiektowe
80
>>> isFinite(12)
true
>>> isFinite(1e308)
true
>>> isFinite(1e309)
false
Jeli dziwi Ci dwa ostatnie wyniki, przypominam, e zgodnie z tym, co napisaem w poprzed-
nim rozdziale, najwiksz dopuszczaln liczb w jzyku JavaScript jest
1.7976931348623157e+308
.
Encode/Decode URIs
W adresach URL (Uniform Resource Locator) i URI (Uniform Resource Identifier) niektóre znaki
maj specjalne znaczenie. Jeli chcemy mie pewno, e zostan one zapisane poprawnie
(czyli jeli chcemy zastosowa sekwencj uniku), moemy skorzysta z funkcji
encodeURI()
lub
encodeURIComponent()
. Pierwsza z nich zwróci poprawny adres URL, druga zaoy, e przeka-
zany jej parametr jest tylko czci URL (na przykad zawiera parametry dania), i odpo-
wiednio zakoduje wszystkie nietypowe znaki.
>>> var url = 'http://www.packtpub.com/scr ipt.php?q=this and that';
>>> encodeURI(url);
"http://www.packtpub.com/scr%20ipt.php?q=this%20and%20that"
>>> encodeURIComponent(url);
"http%3A%2F%2Fwww.packtpub.com%2Fscr%20ipt.php%3Fq%3Dthis%
20and%20that"
Dziaanie przeciwne do
encodeURI()
i
encodeURIComponent()
maj funkcje
decodeURI()
i
decode
´URIComponent()
. W starszym kodzie mona natkn si na starsze funkcje
escape()
i
unescape()
,
jednak s one przestarzae i nie naley ich stosowa.
eval()
Funkcja
eval()
pobiera acuch znaków i uruchamia go jako kod w jzyku JavaScript:
>>> eval('var ii = 2;')
>>> ii
2
eval('var ii = 2;')
dziaa dokadnie tak samo jako
var ii = 2;
Rozdzia 3. • Funkcje
81
S sytuacje, w których
eval()
si przydaje, jednak w miar moliwoci naley tej funkcji uni-
ka. Z reguy mona zastosowa inne rozwizania, które w wikszoci przypadków s bardziej
eleganckie i atwiejsze w utrzymaniu. Weterani JavaScriptu jak mantr powtarzaj zdanie
„
eval
is evil” („
eval
to samo zo”). Mona wymieni nastpujce wady tej funkcji:
Q
Wydajno: wykonywanie kodu „na ywo” jest wolniejsze od wykonywania kodu
zapisanego w skrypcie.
Q
Bezpieczestwo: JavaScript ma due moliwoci, co oznacza, e przy jego
„pomocy” mona co zepsu. Jeli nie moesz ufa ródu, z którego pochodzi
wejcie przekazywane do
eval()
, nie wywouj tej funkcji.
Bonus — funkcja alert()
Spójrzmy jeszcze na bardzo popularn funkcj
alert()
. Nie naley ona do rdzenia jzyka (nie
ma jej w specyfikacji ECMA), ale mona z niej korzysta w rodowisku przegldarki. Pozwala
ona na wywietlanie komunikatów w okienku dialogowym. Czasami przydaje si to podczas
testowania i debugowania aplikacji, chocia w tym celu lepiej korzysta z debugera Firebug.
Na poniszym rysunku wida efekt wykonania kodu
alert("halo!")
.
Pamitaj tylko, e okienko dialogowe blokuje wtek przegldarki, co oznacza, e aden inny
kod nie zostanie wykonany, zanim uytkownik nie kliknie OK. Jeli aplikacja jest czsto aktu-
alizowan aplikacj AJAX, to
alert()
nie jest najlepszym pomysem.
Zasig zmiennych
Warto zwróci uwag, zwaszcza, jeli jest si osob, która wczeniej programowaa w innym
jzyku, e zmienne w jzyku JavaScript nie s definiowane w obrbie bloku, tylko funkcji.
Oznacza to, e jeli zmienna zostaa zdefiniowana wewntrz funkcji, nie jest widoczna poza
ni. Natomiast zmienna zdefiniowana wewntrz bloku
if
lub
for
jest widoczna poza blokiem.
Zmienne globalne to zmienne uywane poza funkcjami, natomiast zmienne lokalne to zmienne
uywane wewntrz funkcji. Kod wewntrz funkcji ma dostp zarówno do zmiennych global-
nych, jak i do swoich zmiennych lokalnych.
JavaScript. Programowanie obiektowe
82
W poniszym przykadzie:
Q
funkcja
f()
ma dostp do zmiennej
global
,
Q
poza funkcj
f()
zmienna
local
nie istnieje.
var global = 1;
function f() {
var local = 2;
global++;
return global;
}
>>> f();
2
>>> f();
3
>>> local
local is not defined
Ponadto naley mie na uwadze, e jeli do deklaracji zmiennej nie zostanie uyta instrukcja
var
, zmienna bdzie miaa zasig globalny. Spójrzmy na przykad:
Co si stao? Funkcja
f()
zawiera zmienn
local
. Przed wywoaniem funkcji zmienna nie ist-
nieje. Jednak podczas pierwszego wywoania funkcji zmienna jest tworzona i ma zasig glo-
balny. Dlatego jeli wówczas spróbujemy sign do zmiennej
local
, okae si ona dostpna.
Dobre rady
Staraj si ogranicza liczb zmiennych globalnych. Wyobra
sobie dwie osoby pracujce nad dwiema
rónymi funkcjami w tym samym skrypcie, które przypadkowo postanawiaj nada t sam nazw
zmiennej globalnej. Moe to doprowadzi do nieoczekiwanych wyników i trudnych do wykrycia bdów.
Zawsze deklaruj zmienne za pomoc instrukcji var.
Rozdzia 3. • Funkcje
83
Poniszy przykad ilustruje wany aspekt podziau na zmienne lokalne i globalne.
var a = 123;
function f() {
alert(a);
var a = 1;
alert(a);
}
f();
By moe spodziewasz si, e pierwszy
alert()
wywietli
123
(warto globalnej zmiennej
a
),
a drugi wywietli
1
(warto lokalnej zmiennej
a
). Jednak stanie si inaczej. Pierwszy
alert()
pokae
"undefined"
. Stanie si tak dlatego, e wewntrz funkcji zasig lokalny jest waniejszy
od globalnego. Zmienna lokalna nadpisuje zmienn globaln o tej samej nazwie. Podczas wy-
konywania pierwszego
alert()
,
a
nie byo jeszcze zdefiniowane (std warto
undefined
), ale
ju istniao w lokalnej przestrzeni nazw.
Funkcje s danymi
Zrozumienie tego punktu widzenia bdzie na pó niejszym etapie bardzo wane — funkcje
tak naprawd s danymi. Oznacza to, e nastpujce dwie metody definiowania funkcji s
równowane:
function f(){return 1;}
var f = function(){return 1;}
Drugi z pokazanych sposobów definiowania funkcji okrela si mianem zapisu literaowego
funkcji. Jeli na zmiennej, której zostaa przypisana warto bdca funkcj, wywoamy ope-
rator
typeof
, zwróci on acuch znaków
"function"
.
>>> function f(){return 1;}
>>> typeof f
"function"
Zatem: funkcje w jzyku JavaScript s specjalnym typem danych. Posiadaj dwie istotne cechy:
Q
zawieraj kod,
Q
s wykonywalne (mog by wywoywane).
Wiesz ju, e funkcje wywouje si poprzez podanie nawiasu po ich nazwie. Nastpny przy-
kad pokazuje, e ta metoda zadziaa niezalenie od sposobu definicji funkcji. Wida w nim
take, e funkcja jest traktowana jak normalna warto, któr mona przypisa nowej zmien-
nej lub nawet wykasowa.
>>> var sum = function(a, b) {return a + b;}
>>> var add = sum;
>>> delete sum
true
JavaScript. Programowanie obiektowe
84
>>> typeof sum;
"undefined"
>>> typeof add;
"function"
>>> add(1, 2);
3
Poniewa funkcje to dane przypisane do zmiennych, stosujemy t sam konwencj nazw co
przy nazywaniu zmiennych — nazwa funkcji nie moe zaczyna si liczb i moe zawiera
dowoln kombinacj liter, cyfr oraz znaku podkrelnika.
Funkcje anonimowe
JavaScript pozwala na rozrzucanie fragmentów danych po caym programie. Wyobra sobie,
e Twój program zawiera nastpujcy fragment kodu:
>>> "test"; [1,2,3]; undefined; null; 1;
Kod wyglda do dziwnie, poniewa nie robi nic poytecznego, jednak jest poprawny i nie
spowoduje bdu. Mona powiedzie, e zawiera dane anonimowe, czyli nieprzypisane do
adnej zmiennej i nieposiadajce nazwy.
Wiesz ju, e funkcje mona traktowa jak wszystkie inne dane. W zwizku z tym ich take
mona uywa bez podania nazwy:
>>> function(a){return a;}
Anonimowe fragmenty danych w kodzie nie mog by zbyt przydatne, chyba e s funkcjami.
W takim wypadku istniej dwa bardzo eleganckie zastosowania tych danych:
Q
Funkcj anonimow mona przekaza jako parametr do innej funkcji. Funkcja
odbierajca ten parametr moe przeprowadzi operacje na otrzymanej funkcji.
Q
Funkcje anonimowe mone definiowa i od razu uruchamia.
Przyjrzyjmy si uwaniej obu zastosowaniom funkcji anonimowych.
Wywoania zwrotne
Skoro funkcje to dane, które mona przypisa zmiennym, to mona je definiowa, kasowa,
kopiowa… Dlaczego zatem nie miaoby by moliwe przekazywanie ich jako parametrów do
innych funkcji?
Rozdzia 3. • Funkcje
85
Oto przykad funkcji, która pobiera dwie funkcje jako parametry, wywouje je, po czym zwra-
ca wynik bdcy sum zwróconych przez nie wartoci:
function wywolaj_i_dodaj(a, b){
return a() + b();
}
Zdefiniujmy teraz dwie pomocnicze funkcje, które bd zwracay ustalone wartoci:
function jeden() {
return 1;
}
function dwa() {
return 2;
}
Moemy przekaza je oryginalnej funkcji i obejrze wynik:
>>> wywolaj_i_dodaj(jeden, dwa);
3
Jako parametry mona take przekazywa funkcje anonimowe. Wówczas zamiast definiowania
jeden()
i
dwa()
wystarczyoby napisa:
wywolaj_i_dodaj(function(){return 1;}, function(){return 2;})
Jeli funkcja A zostaje przekazana funkcji B i B wywouje A, czsto mówi si, e A jest wy-
woaniem zwrotnym (ang. callback function). Jeli A nie ma nazwy, to jest anonimowym wy-
woaniem zwrotnym.
Jakie zastosowania maj takie funkcje? Spójrzmy na przykady, które ilustruj nastpujce
zalety wywoa zwrotnych:
Q
Mona przekazywa funkcje bez koniecznoci ich nazywania, co oznacza, e
potrzebnych jest mniej zmiennych globalnych.
Q
Jeli przeniesiemy obowizek wywoania funkcji na inn funkcj, nasz kod bdzie
krótszy.
Q
Wywoania zwrotne mog korzystnie wpyn na wydajno aplikacji.
Przykady wywoa zwrotnych
Przeanalizujmy czsty scenariusz: mamy funkcj, która zwraca warto, przekazywan na-
stpnie kolejnej funkcji. W naszym przykadzie pierwsza funkcja,
pomnozRazyDwa()
, przyjmuje
trzy parametry, przechodzi przez nie w ptli oraz zwraca tablic zawierajc wynik. Druga
funkcja,
dodajJeden()
, pobiera warto, dodaje do niej jeden, po czym zwraca wynik.
JavaScript. Programowanie obiektowe
86
function pomnozRazyDwa(a, b, c) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = arguments[i] * 2;
}
return ar;
}
function dodajJeden(a) {
return a + 1;
}
Przetestujmy te funkcje:
>>> pomnozRazyDwa(1, 2, 3);
[2, 4, 6]
>>> dodajJeden(100)
101
Zaómy teraz, e chcemy, by tablica
myarr
zawieraa trzy elementy, z których kady przejdzie
przez obie funkcje. Zacznijmy od
pomnozRazyDwa()
.
>>> var myarr = [];
>>> myarr = pomnozRazyDwa(10, 20, 30);
[20, 40, 60]
Moemy teraz wywoywa funkcj
dodajJeden()
w ptli, raz dla kadego elementu tablicy:
>>> for (var i = 0; i < 3; i++) {myarr[i] = addOne(myarr[i]);}
>>> myarr
[21, 41, 61]
Wszystko zadziaa, ale jest tu pole do poprawek. Po pierwsze, przykad uruchamia dwie ptle,
które mog by kosztowne, jeli powtórze jest wiele. dany wynik mona otrzyma przy
uyciu jednej tylko ptli. Oto, jak zmieni funkcj
pomnozRazyDwa()
tak, by jako parametr
przyjmowaa funkcj i wywoywaa j przy kadej iteracji:
function pomnozRazyDwa(a, b, c, callback) {
var i, ar = [];
for(i = 0; i < 3; i++) {
ar[i] = callback(arguments[i] * 2);
}
return ar;
}
Zmieniona wersja funkcji pozwala na wykonanie tej samej pracy przy pomocy jednego wy-
woania. Przekazuje si do niego wartoci pocztkowe oraz funkcj, która ma zosta wywoana
na kadej z tych wartoci.
Rozdzia 3. • Funkcje
87
>>> myarr = pomnozRazyDwa(1, 2, 3, dodajJeden);
[3, 5, 7]
Zamiast definiowania funkcji
dodajJeden()
mona skorzysta z funkcji anonimowej, dziki
czemu zdefiniowana zostanie jedna zmienna globalna mniej.
>>> myarr = pomnozRazyDwa(1, 2, 3, function(a){return a + 1});
[3, 5, 7]
Oczywicie tej samej funkcji mona jako parametr przekaza róne funkcje anonimowe:
>>> myarr = multiplyByTwo(1, 2, 3, function(a){return a + 2});
[4, 6, 8]
Funkcje samowywoujce si
Omówilimy ju funkcje anonimowe i wywoania zwrotne. Przejd my teraz do innego zastosowa-
nia funkcji anonimowych — wywoywania funkcji zaraz po ich zdefiniowaniu. Oto przykad:
(
function(){
alert('uuu!');
}
)()
Pocztkowo moe to wyglda gro nie, ale tak naprawd to proste — funkcj anonimow
umieszcza si w nawiasie, po którym nastpuje inny nawias (w przykadzie jest pusty). Drugi
nawias oznacza „uruchom teraz”. To w nim umieszcza si ewentualne parametry funkcji.
(
function(imie){
alert('Cze ' + imie + '!');
}
)('stary')
Jedn z zalet samowywoujacych si funkcji anonimowych jest to, e kod zostanie wykonany
bez tworzenia nadmiaru zmiennych. Minus jest taki, e tej samej funkcji nie da si uruchomi
dwukrotnie (chyba e znajdzie si wewntrz ptli lub innej funkcji). Dlatego anonimowe funkcje
samowywoujce najlepiej nadaj si do wykonywania jednokrotnych zada inicjalizacyjnych.
Funkcje wewntrzne (prywatne)
Skoro funkcje s zwykymi wartociami, nic nie stoi na przeszkodzie, by zdefiniowa funkcj
wewntrz innej funkcji.
JavaScript. Programowanie obiektowe
88
function a(param) {
function b(theinput) {
return theinput * 2;
};
return 'Wynik wynosi ' + b(param);
};
Stosujc drug notacj definiowania funkcji, moemy napisa:
var a = function(param) {
var b = function(theinput) {
return theinput * 2;
};
return 'Wynik wynosi ' + b(param);
};
Kiedy globalna funkcja
a()
zostanie wywoana, wywoa take lokaln funkcj
b()
. Jako e
b()
jest lokalna, nie jest dostpna spoza
a()
, dlatego nazywamy j funkcj prywatn.
>>> a(2);
"The result is 4"
>>> a(8);
"The result is 16"
>>> b(2);
b is not defined
Ze stosowania funkcji prywatnych pyn nastpujce korzyci:
Q
Nie dochodzi do zamiecenia globalnej przestrzeni nazw (zmniejszone ryzyko
kolizji nazw).
Q
Prywatno: na zewntrz widoczne s tylko te funkcje, które programista chce
udostpni. Funkcjonalnoci nieprzeznaczone dla reszty aplikacji s ukryte.
Funkcje, które zwracaj funkcje
Wspominaem ju, e funkcja zawsze zwraca warto, a jeli nie robi tego w sposób jawny, to
niejawnie zwracana jest warto
undefined
. Funkcja zwraca dokadnie jedn warto, która
z powodzeniem moe by inn funkcj.
function a() {
alert('A!');
return function(){
alert('B!');
};
}
Rozdzia 3. • Funkcje
89
Widoczna powyej funkcja
a()
wykonuje swoj prac (mówi
'A!'
) i zwraca inn funkcj, która
robi co innego (mówi
'B!'
). Wynik mona przypisa jakiej zmiennej i uywa jej jako nor-
malnej funkcji.
>>> var newFunc = a();
>>> newFunc();
Pierwsza linia powyszego kodu spowoduje wywietlenie okienka z wiadomoci
'A!'
, a dru-
ga — okienka z wiadomoci
'B!'
.
Jeli funkcja zwracana przez inn funkcj ma zosta wykonana natychmiast, bez potrzeby
przypisywania jej do nowej zmiennej, wystarczy doda jeszcze jeden nawias. Wynik kocowy
bdzie taki sam jak wczeniej.
>>> a()();
Funkcjo, przepisz e si!
Poniewa funkcje potrafi zwraca funkcje, moliwe jest zastpienie oryginalnej funkcji t
zwracan. Wrómy do poprzedniego przykadu. Warto zwrócon przez wywoanie
a()
mona przypisa zmiennej
a
, nadpisujc w ten sposób istniejc funkcj:
>>> a = a();
Powysza linia przy pierwszym uruchomieniu spowoduje wywietlenie
'A!'
, jednak jej dru-
gie uruchomienie wywietli
'B!'
.
Opisany mechanizm jest przydatny, jeli funkcja wykonuje pewne jednorazowe zadanie. Po
zakoczeniu zadania zmiennej przechowujcej funkcj przypisywana jest nowa warto,
dziki czemu operacje nie musz by powtarzane za kadym razem, gdy kto wywoa funkcj.
W ostatnim przykadzie funkcja zostaa przedefiniowana z zewntrz — pobralimy zwrócon
warto i przypisalimy j funkcji. Jednake moliwe jest równie przepisanie funkcji od rodka.
function a() {
alert('A!');
a = function(){
alert('B!');
};
}
Przy pierwszym wywoaniu funkcja:
Q
Wywietli
'A!'
(zaómy, e to wanie jest nasze jednorazowe zadanie
inicjalizacyjne).
Q
Zmieni definicj globalnej zmiennej
a
, przypisujc jej now funkcj.
Kade kolejne wywoanie bdzie powodowao wywietlenie
'B!'
.
JavaScript. Programowanie obiektowe
90
Oto inny przykad, który czy kilka technik omówionych na ostatnich kilku stronach:
var a = function() {
function inicjalizacja(){
var setup = 'ju';
}
function normalnaPraca() {
alert('praca wre!');
}
inicjalizacja();
return normalnaPraca;
}();
W przykadzie:
Q
Mamy funkcje prywatne:
inicjalizacja()
i
normalnaPraca()
.
Q
Mamy funkcj samowywoujc si: funkcja
a()
jest wywoywana dziki nawiasowi
po jej definicji.
Q
Pierwsze wywoanie
a()
polega na wywoaniu funkcji
inicjalizacja()
i zwróceniu
referencji do zmiennej
normalnaPraca
, która jest funkcj. Zwró uwag na brak
nawiasów przy zwracanej wartoci — nie ma ich dlatego, e zwracamy do funkcji
referencj, a nie wynik wywoania teje funkcji.
Q
Jako e kod zaczyna si od
var a =
, warto zwrócona przez samowywoujc si
funkcj zostanie przypisana zmiennej
a
.
Jeli chcesz sprawdzi, czy poprawnie rozumiesz omówiony zakres materiau, spróbuj odpo-
wiedzie na ponisze pytania. Jakie bdzie zachowanie napisanego przed chwil programu, gdy:
Q
zostanie wgrany po raz pierwszy?
Q
po wgraniu zostanie wywoane
a()
?
Przedstawione mechanizmy okazuj si bardzo przydatne w rodowisku przegldarki. Róne
przegldarki mog realizowa konkretne zadania na róne sposoby. Przy zaoeniu, e waci-
woci przegldarki nie zmieni si pomidzy wywoaniami funkcji, moemy stworzy funkcj,
która wybierze sposób dziaania najlepiej dopasowany do danej przegldarki, po czym w od-
powiedni sposób zmieni swoj definicj, dziki czemu tylko raz bdzie musiaa wykrywa typ
przegldarki. Konkretne przykady zastosowania tego scenariusza bdzie mona zobaczy na
dalszych stronach ksiki.
Domknicia
Pozostaa cz tego rozdziau jest powicona domkniciom (czy istnieje lepszy sposób na
zamknicie rozdziau?). Domknicia pocztkowo mog wydawa si trudne do zrozumienia,
dlatego nie zniechcaj si, jeli nie pojmiesz wszystkiego od razu. Postaraj si doczyta rozdzia
Rozdzia 3. • Funkcje
91
do koca i poeksperymentowa z przykadami, a jeli niektóre zagadnienia nadal nie bd ja-
sne, moesz do nich wróci pó niej, kiedy inne mechanizmy omówione w tym rozdziale nie
bd ju sprawiay Ci adnego kopotu.
Zanim zajmiemy si domkniciami, powtórzmy i rozszerzmy troch pojcia zakresu w jzyku
JavaScript.
acuch zakresów
Jak ju Ci wiadomo, JavaScript nie wyrónia adnych zakresów ograniczonych nawiasami
klamrowymi, ale istnieje zakres funkcji. Zmienna zdefiniowana wewntrz funkcji nie jest wi-
doczna poza t funkcj, natomiast zmienna zdefiniowana wewntrz bloku kodu (np. po
if
lub
w ptli
for
) jest dostpna poza blokiem.
>>> var a = 1; function f(){var b = 1; return a;}
>>> f();
1
>>> b
b is not defined
Zmienna
a
naley do globalnej przestrzeni nazw, podczas gdy zmienna
b
tylko do zakresu
funkcji
f()
. Dlatego:
Q
Wewntrz
f()
widoczne s zarówno
a
i
b
.
Q
Wewntrz
f()
widoczna jest zmienna
a
, ale nie zmienna
b
.
Jeli zdefiniujesz funkcj
n()
osadzon w
f()
,
n()
bdzie miaa dostp do zmiennych ze swo-
jego zakresu, a take do zmiennych swoich „rodziców”. W takim wypadku mówimy o acuchu
zakresów, który moe by dowolnie dugi (gboki).
var a = 1;
function f(){
var b = 1;
function n() {
var c = 3;
}
}
Zasig leksykalny
Funkcje w jzyku JavaScript maj zasig leksykalny. Oznacza to, e funkcje tworz swoje wa-
sne rodowisko (zakres) podczas definicji, a nie podczas wywoania. Spójrzmy na przykad:
JavaScript. Programowanie obiektowe
92
>>> function f1(){var a = 1; f2();}
>>> function f2(){return a;}
>>> f1();
a is not defined
Wewntrz funkcji
f1()
wywoujemy funkcj
f2()
. Poniewa zmienna lokalna
a
znajduje si
take wewntrz
f1()
, kto mógby si spodziewa, e
f2()
bdzie miaa dostp do
a
, jednak
tak nie jest. W momencie definicji
f2()
(a nie w momencie wywoania) nigdzie nie byo ladu
a
.
f2()
, podobnie jak
f1()
, ma dostp jedynie do wasnego zakresu oraz do zakresu globalnego.
f1()
i
f2()
nie wspódziel zakresów lokalnych.
Podczas definiowania funkcja zapamituje swoje rodowisko, to znaczy swój acuch zakresów.
Nie znaczy to wcale, e funkcja pamita kad konkretn zmienn, która pojawia si w tym
zakresie. Wrcz przeciwnie — zmienne mona dodawa, usuwa i uaktualnia, a funkcja zawsze
bdzie widziaa najnowszy, aktualny stan zmiennych. Jeli rozszerzymy przykad o deklaracj
globalnej zmiennej
a
, stanie si ona widoczna dla
f2()
, poniewa
f2()
zna ciek do zmiennych
globalnych i ma dostp do caoci tego rodowiska. Zwró uwag na to, e
f1()
zawiera wywo-
anie
f2()
, które dziaa — mimo e
f2()
nie zostaa jeszcze zdefiniowana.
f1()
musi tylko po-
siada wiedz o wasnym zakresie, by wszystko, co si w nim pojawi, stawao si automatycznie
dostpne dla
f1()
.
>>> function f1(){var a = 1; f2();}
>>> function f2(){return a;}
>>> f1();
a is not defined
>>> var a = 5;
>>> f1();
5
>>> a = 55;
>>> f1();
55
>>> delete a;
true
>>> f1();
a is not defined
Przedstawiony mechanizm sprawia, e JavaScript jest bardzo elastyczny — mona dodawa
zmienne, usuwa je, a potem dodawa je ponownie. Moesz poeksperymentowa, kasujc
funkcj
f2()
, a potem definiujc j ponownie, ale z innym ciaem. Funkcja
f1()
nadal bdzie
dziaa, poniewa musi zna jedynie sposób dostpu do swojego zakresu — nie jest jej po-
trzebna wiedza o tym, co kiedy do tego zakresu naleao. Cig dalszy przykadu:
Rozdzia 3. • Funkcje
93
true
>>> f1()
f2 is not defined
>>> var f2 = function(){return a * 2;}
>>> var a = 5;
5
>>> f1();
10
Przerwanie acucha za pomoc domknicia
Zaczniemy od ilustracji.
Poniej widzisz zakres globalny. Wyobra go sobie jako wszechwiat.
Moe on zawiera zmienne, takie jak
a
, i funkcje, jak
F
.
Funkcje posiadaj wasn przestrze, któr mog wykorzystywa do przechowywania innych
zmiennych (i funkcji). W pewnym momencie rysunek bdzie wyglda mniej wicej tak:
JavaScript. Programowanie obiektowe
94
Jeli jeste w punkcie
a
, jeste w przestrzeni globalnej. Jeli w punkcie
b
, który naley do
przestrzeni funkcji
F
, masz dostp do przestrzeni globalnej oraz do przestrzeni
F
. Jeli znala-
ze si w punkcie
c
, który naley do funkcji
N
, moesz sign do przestrzeni globalnej, prze-
strzeni
F
oraz
N
. Nie da si sign z
a
do
b
, poniewa punkt
b
nie jest widoczny poza
F
. Moesz
natomiast uzyska dostp z
c
do
b
lub z
N
do
b
. Ciekawe rzeczy (domknicie) zaczynaj si
dzia, gdy jakim sposobem
N
wydostaje si z
F
i trafia do przestrzeni globalnej.
Co si wtedy dzieje?
N
jest w tej samej przestrzeni globalnej co
a
. Jako e funkcje pamitaj
rodowisko, w którym zostay zdefiniowane,
N
nadal ma dostp do przestrzeni
F
, a co za tym
idzie dostp do
b
. Jest to ciekawe dlatego, e
N
znajduje si tam gdzie
a
, a jednak
N
ma dostp
do
b
, za
a
nie.
Jak
N
udaje si przerwa acuch? Istniej dwa sposoby:
N
moe zosta zmienn globaln
(pominicie
var
) lub moe zosta zwrócona przez
F
do przestrzeni globalnej. Zobaczmy, jak to
wyglda w praktyce.
Domknicie 1.
Przyjrzyj si uwanie tej funkcji:
function f(){
var b = "b";
return function(){
return b;
}
}
Rozdzia 3. • Funkcje
95
Funkcja zawiera lokaln zmienn
b
, która nie jest dostpna z przestrzeni globalnej:
>>> b
b is not defined
Zwró uwag na warto zwracan przez
f()
: jest ona inn funkcj. Moesz o niej myle jako
o
N
z przedstawionych powyej rysunków. Nowa funkcja ma dostp do swojej przestrzeni
prywatnej, do przestrzeni funkcji
f()
oraz do przestrzeni globalnej. Widzi zatem równie
b
.
Poniewa
f()
mona wywoa w przestrzeni globalnej (jest funkcj globaln), moesz j wy-
woa i przypisa zwracan przez ni warto innej zmiennej globalnej. Wynikiem bdzie
nowa funkcja globalna, która ma dostp do prywatnej przestrzeni
f()
.
>>> var n = f();
>>> n();
"b"
Domknicie 2.
Przykad, który nastpi za chwil, pozwala uzyska ten sam wynik co przykad wczeniejszy,
jednak z zastosowaniem nieco innych metod. Funkcja
f()
nie bdzie zwracaa funkcji, a za-
miast tego utworzy now, globaln funkcj
n()
wewntrz swojego ciaa.
Zacznijmy od deklaracji zmiennej, do której pó niej przypiszemy now funkcj. Nie jest to
obowizkowe, ale zawsze warto deklarowa zmienne. Definicja funkcji
f()
moe wyglda tak:
var n;
function f(){
var b = "b";
n = function(){
return b;
}
}
Co si stanie po wywoaniu
f()
?
>>> f();
Wewntrz przestrzeni
f()
definiowana jest nowa funkcja. Poniewa nie zostaa uyta instruk-
cja
var
, funkcja jest globalna. W czasie definicji funkcja
n()
znajdowaa si wewntrz
f()
, zatem
ma dostp do zakresu zmiennych
f()
.
n()
zachowa prawo dostpu nawet wtedy, gdy stanie si
czci przestrzeni globalnej.
>>> n();
"b"
JavaScript. Programowanie obiektowe
96
Domknicie 3. i jedna definicja
W oparciu o to, co zostao powiedziane do tej pory, moemy powiedzie, e domknicie jest
tworzone, gdy funkcja zachowuje dostp do zakresu rodzica po tym, jak rodzic zwróci j do
globalnej przestrzeni nazw.
Argument przekazany funkcji wewntrz niej jest dostpny jako zmienna globalna. Moesz stwo-
rzy funkcj zwracajc inn funkcj, która z kolei zwraca argument przekazany rodzicowi.
function f(arg) {
var n = function(){
return arg;
};
arg++;
return n;
}
Funkcj mona wywoa w nastpujcy sposób:
>>> var m = f(123);
>>> m();
124
Zauwa, e zmienna
arg
zostaa zwikszona ju po definicji funkcji, a pomimo tego
m()
zwrócia
aktualn warto. Jest to kolejny dowód na to, e funkcje s zwizane ze swoimi zakresami, a nie
z przechowywanymi tam w danym momencie zmiennymi i ich wartociami.
Domknicia w ptli
Poka teraz co, co czsto prowadzi do bardzo trudnych do wykrycia bdów, poniewa na
pierwszy rzut oka wydaje si, e nie ma tam miejsca na pomyk.
Napiszmy ptl o trzech iteracjach, która za kadym przebiegiem zwraca numer ptli. Funk-
cje zostan dodane do tablicy, która na koniec zostanie zwrócona. Oto nasza funkcja:
function f() {
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = function(){
return i;
}
}
return a;
}
Rozdzia 3. • Funkcje
97
Wywoajmy j teraz, przypisujc wynikow tablic zmiennej
a
.
>>> var a = f();
Mamy zatem tablic z trzema funkcjami. Wywoajmy je, podajc nawiasy po kadym elemen-
cie tablicy. Oczekiwane zachowanie to wypisanie numerów iteracji:
0
,
1
i
2
. Spróbujmy:
>>> a[0]()
3
>>> a[1]()
3
>>> a[2]()
3
Hm, niezupenie to mielimy na myli. Co si stao? Utworzylimy trzy domknicia, które
wskazuj na t sam lokaln zmienn
i
. Domknicia nie pamitaj wartoci, tylko przechowuj
referencj do zmiennej
i
— dlatego zwracaj jej aktualn warto. Po wyjciu z ptli wartoci
zmiennej
i
jest 3. Wszystkie funkcje wskazuj na t sam warto.
(Dla lepszego zrozumienia ptli zastanów si, dlaczego wartoci
i
jest
3
, a nie
2
).
Jak zatem zaimplementowa poprawne zachowanie? Potrzebne nam s trzy róne zmienne.
Eleganckie rozwizanie polega na wykorzystaniu kolejnego domknicia:
function f() {
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = (function(x){
return function(){
return x;
}
})(i);
}
return a;
}
Uzyskamy oczekiwany wynik:
>>> var a = f();
>>> a[0]();
0
>>> a[1]();
1
JavaScript. Programowanie obiektowe
98
>>> a[2]();
2
W tej wersji nie tworzymy funkcji zwracajcej
i
, tylko przekazujemy
i
innej, samowywouj-
cej si funkcji. W tej funkcji
i
staje si lokaln zmienn
x
i za kadym razem ma inn warto.
Ten sam wynik mona uzyska przy uyciu „normalnej” (czyli niesamowywoujcej si) funkcji
wewntrznej. Kluczem do sukcesu jest wykorzystanie rodkowej funkcji do ustalenia wartoci
i
podczas danej iteracji.
function f() {
function makeClosure(x) {
return function(){
return x;
}
}
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
Funkcje dostpowe
Chc opowiedzie o jeszcze dwóch sposobach wykorzystania domkni. Pierwszy z nich po-
lega na utworzeniu funkcji dostpowych get (pobranie wartoci) i set (ustawienie wartoci).
Zaómy, e posiadasz zmienn, która moe przyjmowa wartoci tylko ze cile okrelonego
zbioru. Nie chcesz odkrywa tej zmiennej, poniewa chcesz zabezpieczy si przed sytuacj,
w której pewien fragment kodu nada jej niedozwolon warto. Rozwizaniem jest utworze-
nie schronienia dla tej zmiennej wewntrz pewnej funkcji i stworzenie dwóch dodatkowych
funkcji, które bd odczytyway i ustawiay jej warto. Funkcja ustawiajca warto moe
zawiera pewn logik, która nie pozwoli na nadanie zmiennej wartoci spoza dozwolonego
zbioru (jednak dla uproszczenia przykadu pomimy walidacj).
Funkcje dostpowe powinny znale si wewntrz tej samej funkcji, która zawiera tajn zmienn,
tak by dzieliy ten sam zakres:
var getValue, setValue;
(function() {
var secret = 0;
getValue = function(){
return secret;
};
Rozdzia 3. • Funkcje
99
setValue = function(v){
secret = v;
};
})()
Funkcja, która opakowuje zmienn i dwie funkcje dostpowe, jest tutaj samowywoujc si
funkcj anonimow. Definiuje ona
setValue()
i
getValue()
jako funkcje globalne, podczas
gdy zmienna
secret
pozostaje lokalna i nie jest dostpna bezporednio.
>>> getValue()
0
>>> setValue(123)
>>> getValue()
123
Iterator
Ostatni przykad domknicia (a zarazem ostatni przykad w tym rozdziale) pokazuje wykorzy-
stanie domkni w celu osignicia funkcjonalnoci iteratora.
Wiesz ju, jak wykorzysta ptl do przejcia przez wszystkie elementy zwykej tablicy. Mo-
esz jednak napotka bardziej zoon struktur danych, w której kolejno elementów jest
okrelana przez bardziej zoony zestaw regu. Wówczas skomplikowan logik rozwizujc
problem „kto nastpny?” umieszczasz w wygodnej w uyciu funkcji
next()
. Nastpnie wywou-
jesz
next()
za kadym razem, gdy chcesz pobra kolejn warto. Na potrzeby przykadu wy-
korzystamy jednak zwyk tablic, a nie zoon struktur danych.
Oto funkcja inicjalizacyjna, która pobiera tablic, a take definiuje prywatny wska nik
i
, zaw-
sze wskazujcy nastpny element w tablicy:
function setup(x) {
var i = 0;
return function(){
return x[i++];
};
}
Wywoanie funkcji
setup()
z parametrem bdcym tablic danych spowoduje automatyczne
utworzenie funkcji
next()
.
>>> var next = setup(['a', 'b', 'c']);
Dalej czekaj nas sam przyjemnoci: wywoujc wci t sam funkcj, przejdziemy przez
wszystkie elementy tablicy.
JavaScript. Programowanie obiektowe
100
>>> next();
"a"
>>> next();
"b"
>>> next();
"c"
Podsumowanie
Wanie skoczylimy podstawowy kurs poj zwizanych z funkcjami. Przejcie do konceptów
programowania obiektowego oraz do wzorców wykorzystywanych w nowoczesnym progra-
mowaniu w jzyku JavaScript powinno by dla Ciebie proste. Do tej pory unikalimy funkcjo-
nalnoci obiektowych, ale od tej chwili nie bdziemy ju tego robi. Powtórzmy materia przed-
stawiony w tym rozdziale. Omówione zostay nastpujce kwestie:
Q
Definiowanie i wywoywanie funkcji.
Q
Parametry funkcji i ich elastyczno.
Q
Funkcje wbudowane:
parseInt()
,
parseFloat()
,
isNaN()
,
isFinite()
,
eval()
,
a take cztery funkcje do kodowania i dekodowania adresów URL.
Q
Zakres zmiennych: nie ma zakresu zwizanego z nawiasami klamrowymi, istnieje
zakres funkcji, funkcje maj zakres leksykalny, obowizuje zasada acucha zakresów.
Q
Funkcje to dane — funkcj mona przypisa zmiennej, z czego wynika szereg
ciekawych zastosowa, wród których mona wymieni:
Q
prywatne funkcje i zmienne,
Q
funkcje anonimowe,
Q
wywoania zwrotne,
Q
samowywoujce si funkcje,
Q
funkcje zmieniajce swoj definicj.
Q
Domknicia.
wiczenia
1.
Napisz funkcj, która przeksztaca szesnastkow definicj koloru (np. niebieski
to
"0000FF"
) na reprezentacj RGB (np. "rgb(0, 0, 255)"). Nazwij funkcj
getRGB()
i przetestuj j za pomoc nastpujcego kodu:
Rozdzia 3. • Funkcje
101
>>> var a = getRGB("#00FF00");
>>> a;
"rgb(0, 255, 0)"
2.
Co pojawi si w konsoli po uruchomieniu kadej z poniszych linii kodu?
>>> parseInt(1e1)
>>> parseInt('1e1')
>>> parseFloat('1e1')
>>> isFinite(0/10)
>>> isFinite(20/0)
>>> isNaN(parseInt(NaN));
3.
Co pojawi si w okienku
alert()
po wykonaniu nastpujcego kodu?
var a = 1;
function f() {
var a = 2;
function n() {
alert(a);
}
n();
}
f();
4.
Wszystkie ponisze przykady spowoduj wywietlenie
"Uuu!"
. Czy potrafisz
powiedzie dlaczego?
4.1
var f = alert;
eval('f("Uuu!")');
4.2
var e;
var f = alert;
eval('e=f')('Uuu!');
4.3
(
function(){
return alert;
}
)()('Uuu!');