Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00211 005577 13609496 na godz. na dobę w sumie
JavaScript. Programowanie obiektowe - książka
JavaScript. Programowanie obiektowe - książka
Autor: Liczba stron: 336
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-2242-9 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> javascript - programowanie
Porównaj ceny (książka, ebook, audiobook).

Poznaj obiektowe możliwości JavaScript!

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.

Od podstaw do sprawnego programowania obiektowego!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

JavaScript. Programowanie obiektowe Autor: Stoyan Stefanov T³umaczenie: Justyna Walkowska ISBN: 978-83-246-2242-9 Tytu³ orygina³u: Object-Oriented JavaScript 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 treĂci O autorze O recenzentach Przedmowa Co znajdziesz w tej ksiÈĝce? Konwencje Rozdziaï 1. Wprowadzenie TrochÚ historii Zapowiedě zmian TeraěniejszoĂÊ PrzyszïoĂÊ Programowanie obiektowe Obiekty Klasy Kapsuïkowanie Agregacja Dziedziczenie Polimorfizm Programowanie obiektowe — podsumowanie Konfiguracja Ărodowiska rozwijania aplikacji NiezbÚdne narzÚdzia Korzystanie z konsoli Firebug Podsumowanie Rozdziaï 2. Proste typy danych, tablice, pÚtle i warunki Zmienne WielkoĂÊ liter ma znaczenie Operatory 13 15 19 19 20 23 24 25 26 26 27 27 28 28 29 29 30 30 31 31 32 33 35 35 36 37 Spis treĞci Proste typy danych Ustalanie typu danych — operator typeof Liczby Liczby ósemkowe i szesnastkowe Wykïadniki potÚg NieskoñczonoĂÊ NaN ’añcuchy znaków Konwersje ïañcuchów Znaki specjalne Typ boolean Operatory logiczne Priorytety operatorów Leniwe wartoĂciowanie Porównywanie Undefined i null Proste typy danych — podsumowanie Tablice Dodawanie i aktualizacja elementów tablicy Usuwanie elementów Tablice tablic Warunki i pÚtle Bloki kodu Warunki if Sprawdzanie, czy zmienna istnieje Alternatywna skïadnia if Switch PÚtle PÚtla while PÚtla do…while PÚtla for PÚtla for…in Komentarze Podsumowanie mwiczenia Rozdziaï 3. Funkcje Czym jest funkcja? Wywoïywanie funkcji Parametry Funkcje predefiniowane parseInt() parseFloat() isNaN() isFinite() Encode/Decode URIs eval() Bonus — funkcja alert() 6 40 41 41 41 42 43 45 45 46 47 48 49 51 52 53 54 56 56 57 58 58 60 60 61 62 63 63 65 66 66 66 69 70 71 71 73 74 74 74 76 76 78 79 79 80 80 81 Spis treĞci ZasiÚg zmiennych Funkcje sÈ danymi Funkcje anonimowe Wywoïania zwrotne Przykïady wywoïañ zwrotnych Funkcje samowywoïujÈce siÚ Funkcje wewnÚtrzne (prywatne) Funkcje, które zwracajÈ funkcje Funkcjo, przepiszĝe siÚ! DomkniÚcia ’añcuch zakresów ZasiÚg leksykalny Przerwanie ïañcucha za pomocÈ domkniÚcia DomkniÚcie 1. DomkniÚcie 2. DomkniÚcie 3. i jedna definicja DomkniÚcia w pÚtli Funkcje dostÚpowe Iterator Podsumowanie mwiczenia Rozdziaï 4. Obiekty Od tablic do obiektów Elementy, pola, metody Tablice asocjacyjne DostÚp do wïasnoĂci obiektu Wywoïywanie metod obiektu Modyfikacja pól i metod WartoĂÊ this Konstruktory Obiekt globalny Pole constructor Operator instanceof Funkcje zwracajÈce obiekty Przekazywanie obiektów Porównywanie obiektów Obiekty w konsoli Firebug Obiekty wbudowane Object Array Ciekawe metody obiektu Array Function WïasnoĂci obiektu Function Metody obiektu Function Nowe spojrzenie na obiekt arguments Boolean Number 81 83 84 84 85 87 87 88 89 90 91 91 93 94 95 96 96 98 99 100 100 103 103 105 105 106 107 108 109 109 110 112 112 113 114 114 115 117 117 118 120 122 123 125 126 127 128 7 Spis treĞci String Ciekawe metody obiektu String Math Date Metody dziaïajÈce na obiektach Date RegExp Pola obiektów RegExp Metody obiektów RegExp Metody obiektu String, których parametrami mogÈ byÊ wyraĝenia regularne search() i match() replace() Wywoïania zwrotne replace split() Przekazanie zwykïego tekstu zamiast wyraĝenia regularnego Obsïuga bïÚdów za pomocÈ obiektów Error Podsumowanie mwiczenia Rozdziaï 5. Prototypy Pole prototype Dodawanie pól i metod przy uĝyciu prototypu Korzystanie z pól i metod obiektu prototype Wïasne pola obiektu a pola prototypu Nadpisywanie pól prototypu wïasnymi polami obiektu Pobieranie listy pól isPrototypeOf() Ukryte powiÈzanie __proto__ Rozszerzanie obiektów wbudowanych Rozszerzanie obiektów wbudowanych — kontrowersje Puïapki zwiÈzane z prototypami Podsumowanie mwiczenia Rozdziaï 6. Dziedziczenie ’añcuchy prototypów Przykïadowy ïañcuch prototypów Przenoszenie wspólnych pól do prototypu Dziedziczenie samego prototypu Konstruktor tymczasowy — new F() Uber: dostÚp do obiektu-rodzica ZamkniÚcie dziedziczenia wewnÈtrz funkcji Kopiowanie pól Uwaga na kopiowanie przez referencjÚ! Obiekty dziedziczÈ z obiektów GïÚbokie kopiowanie object() PoïÈczenie dziedziczenia prototypowego z kopiowaniem pól 8 130 132 135 136 138 140 141 142 143 143 144 145 146 146 146 150 151 155 155 156 157 158 159 160 162 163 165 166 167 169 170 171 172 172 175 177 178 180 181 182 184 186 187 189 190 Spis treĞci Dziedziczenie wielokrotne Miksiny Dziedziczenie pasoĝytnicze Wypoĝyczanie konstruktora Poĝycz konstruktor i skopiuj jego prototyp Podsumowanie Studium przypadku: rysujemy ksztaïty Analiza Implementacja Testowanie mwiczenia Rozdziaï 7. ¥rodowisko przeglÈdarki ’Èczenie JavaScriptu z kodem HTML BOM i DOM — przeglÈd BOM Ponownie odkrywamy obiekt window window.navigator Firebug jako ĂciÈga window.location window.history window.frames window.screen window.open() i window.close() window.moveTo(), window.resizeTo() window.alert(), window.prompt(), window.confirm() window.setTimeout(), window.setInterval() window.document DOM Core DOM i HTML DOM DostÚp do wÚzïów DOM WÚzeï document documentElement WÚzïy-dzieci Atrybuty DostÚp do zawartoĂci znacznika Uproszczone metody dostÚpowe DOM RówieĂnicy, body, pierwsze i ostatnie dziecko Spacer przez wÚzïy DOM Modyfikacja wÚzïów DOM Modyfikacja stylu Zabawa formularzami Tworzenie nowych wÚzïów Metoda w peïni zgodna z DOM cloneNode() insertBefore() Usuwanie wÚzïów 191 193 193 194 196 197 200 200 201 204 205 207 207 208 209 209 210 210 211 212 213 214 215 216 216 217 219 219 221 222 223 224 224 225 226 227 228 230 230 231 232 233 234 235 236 236 9 Spis treĞci Obiekty DOM istniejÈce tylko w HTML Starsze sposoby dostÚpu do dokumentu document.write() Pola cookies, title, referrer i domain Zdarzenia Kod obsïugi zdarzeñ wpleciony w atrybuty HTML Pola elementów Obserwatorzy zdarzeñ DOM Przechwytywanie i bÈbelkowanie Zatrzymanie propagacji Anulowanie zachowania domyĂlnego Obsïuga zdarzeñ w róĝnych przeglÈdarkach Typy zdarzeñ XMLHttpRequest Wysïanie ĝÈdania Przetworzenie odpowiedzi Tworzenie obiektów XHR w IE w wersjach starszych niĝ 7 A jak asynchroniczny X jak XML Przykïad Podsumowanie mwiczenia Rozdziaï 8. Wzorce kodowania i wzorce projektowe Wzorce kodowania Izolowanie zachowania Warstwa treĂci Warstwa prezentacji Zachowanie Przykïad wydzielenia warstwy zachowania Przestrzenie nazw Obiekt w roli przestrzeni nazw Konstruktory w przestrzeniach nazw Metoda namespace() RozgaïÚzianie kodu w czasie inicjalizacji Leniwe definicje Obiekt konfiguracyjny Prywatne pola i metody Metody uprzywilejowane Funkcje prywatne w roli metod publicznych Funkcje samowywoïujÈce siÚ ’añcuchowanie JSON Wzorce projektowe Singleton Singleton 2 Zmienna globalna Pole konstruktora Pole prywatne 10 238 239 240 240 242 242 242 243 244 246 248 248 249 250 251 252 253 254 254 254 257 258 261 262 262 262 263 263 263 264 264 265 266 267 268 269 270 271 272 273 273 274 275 276 276 277 277 278 Spis treĞci Fabryka Dekorator Dekorowanie choinki Obserwator Podsumowanie Dodatek A Sïowa zarezerwowane Lista sïów zarezerwowanych majÈcych specjalne znaczenie w jÚzyku JavaScript Lista sïów zarezerwowanych na uĝytek przyszïych implementacji Dodatek B Funkcje wbudowane Dodatek C Obiekty wbudowane Object Skïadowe konstruktora Object Skïadowe obiektów tworzonych przez konstruktor Object Array Skïadowe obiektów Array Function Skïadowe obiektów Function Boolean Number Skïadowe konstruktora Number Skïadowe obiektów Number String Skïadowe konstruktora String Skïadowe obiektów String Date Skïadowe konstruktora Date Skïadowe obiektów Date Math Skïadowe obiektu Math RegExp Skïadowe obiektów RegExp Obiekty Error Skïadowe obiektów Error Dodatek D Wyraĝenia regularne Skorowidz 278 280 280 282 285 287 287 288 291 295 295 296 296 298 298 301 301 302 302 303 304 304 305 305 308 308 309 311 312 313 314 315 315 317 323 11 3 Funkcje Opanowanie funkcji ma kluczowe znaczenie podczas nauki kaĝdego jÚzyka programowania, a w przypadku JavaScriptu jest jeszcze waĝniejsze niĝ zwykle. Jest tak dlatego, ĝe w tym jÚ- zyku funkcje majÈ bardzo wiele zastosowañ i w duĝej mierze to dziÚki nim JavaScript jest tak elastyczny i ekspresywny. W miejscach, gdzie w innych jÚzykach programowania trzeba by byïo stosowaÊ specjalnÈ skïadniÚ w celu wykorzystania obiektowoĂci, JavaScript udostÚpnia funkcje. Ten rozdziaï omawia: Q definiowanie funkcji i korzystanie z nich, Q przekazywanie funkcjom parametrów, Q funkcje predefiniowane dostÚpne za darmo, Q zasiÚg zmiennych, Q podejĂcie, zgodnie z którym funkcje to tylko dane specjalnego typu. Zrozumienie powyĝszych tematów da nam solidne oparcie przed przejĂciem do kolejnej czÚ- Ăci rozdziaïu, w której przedstawione zostanÈ pewne ciekawe zastosowania funkcji: Q funkcje anonimowe; Q wywoïania zwrotne; Q samowywoïujÈce siÚ funkcje; Q funkcje wewnÚtrzne (zdefiniowane wewnÈtrz innych funkcji); Q funkcje, które zwracajÈ inne funkcje; Q funkcje, które zmieniajÈ swojÈ definicjÚ; Q domkniÚcia. JavaScript. Programowanie obiektowe Czym jest funkcja? Funkcje pozwalajÈ zgrupowaÊ pewnÈ iloĂÊ kodu, nadaÊ jej nazwÚ, a nastÚpnie ponownie wy- korzystaÊ przy uĝyciu tej wïaĂnie nazwy. Spójrzmy na przykïad: function sum(a, b) { var c = a + b; return c; } Z jakich czÚĂci skïada siÚ funkcja? Q Sïowo kluczowe function. Q Nazwa funkcji, w przykïadzie jest to sum. Q Oczekiwane parametry (argumenty), w tym wypadku a i b. Funkcja moĝe mieÊ ich zero lub wiÚcej. JeĂli jest ich wiÚcej niĝ jeden, parametry rozdziela siÚ przecinkami. Q Blok kodu, nazywany ciaïem funkcji. Q Instrukcja return, która umoĝliwia zwrócenie obliczonej wartoĂci funkcji. Funkcja zawsze zwraca wartoĂÊ. JeĂli nie robi tego w sposób jawny, niejawnie zwraca wartoĂÊ undefined. ZwróÊ uwagÚ, ĝe funkcja moĝe zwróciÊ tylko jednÈ wartoĂÊ. JeĂli potrzebne jest zwrócenie wiÚkszej liczby wartoĂci, naleĝy umieĂciÊ je w tablicy i zwróciÊ tablicÚ jako wartoĂÊ funkcji. Wywoïywanie funkcji Aby skorzystaÊ z funkcji, naleĝy jÈ wywoïaÊ. FunkcjÚ wywoïuje siÚ poprzez podanie jej nazwy i argumentów umieszczonych w nawiasie. Wywoïajmy zatem funkcjÚ sum(), przekazujÈc jej dwa argumenty i przypisujÈc zwracanÈ przez niÈ wartoĂÊ zmiennej result. var result = sum(1, 2); result; 3 Parametry Podczas definiowania funkcji moĝna okreĂliÊ oczekiwane parametry. Funkcja nie musi pobie- raÊ parametrów, ale jeĂli oczekuje, ĝe je otrzyma, a programista podczas wywoïywania funkcji zapomni o ich podaniu, JavaScript przypisze im wartoĂÊ undefined. W poniĝszym przykïadzie funkcja zwraca wartoĂÊ NaN, poniewaĝ próbuje dodaÊ 1 do undefined: 74 Rozdziaá 3. • Funkcje sum(1) NaN JavaScript nie wybrzydza podczas pobierania parametrów. JeĂli otrzyma ich wiÚcej, niĝ jest potrzebne, dodatkowe parametry zostanÈ zignorowane: sum(1, 2, 3, 4, 5) 3 Na dodatek moĝliwe jest pisanie funkcji, które mogÈ przyjmowaÊ róĝnÈ liczbÚ parametrów. Jest to moĝliwe dziÚki tablicy arguments, która jest automatycznie tworzona wewnÈtrz kaĝdej funkcji. Oto funkcja, której dziaïanie 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 przyjmowaïa ona dowolnÈ liczbÚ parametrów i dodawaïa 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; } JeĂli podczas testowania wywoïasz tÚ funkcjÚ z innÈ niĝ wczeĂniej liczbÈ parametrów (lub nawet bez parametrów), zobaczysz, ĝe dziaïa 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 75 JavaScript. Programowanie obiektowe sumaNaSterydach(); 0 Wyraĝenie arguments.length zwraca liczbÚ parametrów podanych podczas wywoïania funkcji. JeĂli nie rozumiesz jego skïadni, nie przejmuj siÚ, wrócimy do tego w nastÚpnym rozdziale. Wtedy takĝe dowiesz siÚ, ĝe arguments w rzeczywistoĂci nie jest tablicÈ, ale obiektem tablico- podobnym. Funkcje predefiniowane Istnieje pewna liczba funkcji, które zostaïy 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 wartoĂciom 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 reguïy podczas korzystania z funkcji Twój program nie musi wiedzieÊ, jakie czynnoĂci sÈ wykonywane wewnÈtrz danej funkcji. Moĝesz myĂleÊ o funkcjach jako o czarnych skrzynkach — podajesz im pewne wartoĂci (w postaci parametrów wejĂciowych) i odbierasz od nich zwracane wyniki. Jest to prawdziwe dla wszystkich funkcji — tych wbudowanych w jÚzyk JavaScript, tych pisanych przez Ciebie oraz tych stworzonych przez Twoich wspóïpracowników lub nieznanych Ci programistów. parseInt() parseInt() pobiera argument dowolnego typu (najczÚĂciej ïañcuch znaków) i próbuje zamie- niÊ go na liczbÚ caïkowitÈ. JeĂli operacja siÚ nie powiedzie, zwrócona zostanie wartoĂÊ NaN. parseInt( 123 ) 123 76 Rozdziaá 3. • Funkcje parseInt( abc123 ) NaN parseInt( 1abc23 ) 1 parseInt( 123abc ) 123 Funkcja pobiera jeszcze opcjonalny drugi argument, który okreĂla podstawÚ, opisujÈcÈ typ liczby: dziesiÚtny, szesnastkowy, binarny itp. Przykïadowo: nie ma sensu próba zamiany po- brania liczby dziesiÚtnej z ïañcucha FF , zatem wynikiem bÚdzie NaN, jednak jeĂli 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 dziesiÚtna) i 8 (liczba ósemkowa). parseInt( 0377 , 10) 377 parseInt( 0377 , 8) 255 JeĂli drugi argument nie zostanie podany, za podstawÚ uznawana jest liczba 10, z nastÚpujÈ- cymi wyjÈtkami: Q JeĂli jako pierwszy argument przekazany zostanie ïañcuch zaczynajÈcy siÚ od 0x, drugiemu argumentowi (jeĂli nie zostaï podany) przypisana zostanie wartoĂÊ 16 (liczba zostanie uznana za szesnastkowÈ). Q JeĂli pierwszy parametr zaczyna siÚ od 0, drugi otrzyma wartoĂÊ 8. parseInt( 377 ) 377 parseInt( 0377 ) 255 parseInt( 0x377 ) 887 77 JavaScript. Programowanie obiektowe Najbezpieczniejszym rozwiÈzaniem jest okreĂlanie podstawy za kaĝdym razem. JeĂli tego nie zrobisz, kod prawdopodobnie zadziaïa w 99 przypadków (poniewaĝ najczÚĂciej parsuje siÚ liczby dziesiÚtne), jednak jeĂli trafisz na liczbÚ zapisanÈ w innym systemie, moĝesz osiwieÊ, zanim uda Ci siÚ znaleěÊ przyczynÚ bïÚdu. Wyobraě sobie na przykïad, ĝe parsujesz pola for- mularza, który reprezentuje kalendarz, i ĝe uĝytkownik wpisaï 08, majÈc na myĂli sierpieñ. JeĂli nie podasz podstawy, otrzymasz wynik inny niĝ oczekiwany. parseFloat() parseFloat() dziaïa podobnie do parseInt(), ale oczekuje uïamkó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 bÚdzie umiaïa sobie poradziÊ, nawet jeĂli pozostaïa czÚĂÊ tekstu zawiera poprawne liczby. parseFloat( a123.34 ) NaN parseFloat( a123.34 ) NaN parseFloat( 12a3.34 ) 12 parseFloat(), w przeciwieñstwie do parseInt(), jest w stanie poprawnie zinterpretowaÊ zapis wykïadniczy. parseFloat( 123e-2 ) 1.23 parseFloat( 123e2 ) 12300 78 Rozdziaá 3. • Funkcje parseInt( 1e10 ) 1 isNaN() Przy pomocy isNaN() moĝna sprawdziÊ, czy wartoĂÊ wejĂciowa jest liczbÈ, której moĝna bez- piecznie uĝywaÊ w operacjach arytmetycznych. isNaN() pozwala w wygodny sposób dowie- dzieÊ siÚ, czy funkcjom parseInt() i parseFloat() udaïo siÚ sparsowaÊ liczbÚ. isNaN(NaN) true isNaN(123) false isNaN(1.23) false isNaN(parseInt( abc123 )) true Ta funkcja takĝe stara siÚ zamieniÊ parametr wejĂciowy na liczbÚ: isNaN( 1.23 ) false isNaN( a1.23 ) true Funkcja isNaN() jest potrzebna takĝe dlatego, ĝe liczba NaN nie jest równa samej sobie. Wyni- kiem porównania NaN === NaN bÚdzie false! isFinite() Funkcja isFinite() sprawdza, czy wartoĂÊ parametru wejĂciowego to liczba róĝna od Infinity i róĝna od NaN. isFinite(Infinity) false isFinite(-Infinity) false 79 JavaScript. Programowanie obiektowe isFinite(12) true isFinite(1e308) true isFinite(1e309) false JeĂli dziwiÈ CiÚ dwa ostatnie wyniki, przypominam, ĝe zgodnie z tym, co napisaïem w poprzed- nim rozdziale, najwiÚkszÈ dopuszczalnÈ liczbÈ w jÚzyku 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. JeĂli chcemy mieÊ pewnoĂÊ, ĝe zostanÈ one zapisane poprawnie (czyli jeĂli chcemy zastosowaÊ sekwencjÚ uniku), moĝemy skorzystaÊ z funkcji encodeURI() lub encodeURIComponent(). Pierwsza z nich zwróci poprawny adres URL, druga zaïoĝy, ĝe przeka- zany jej parametr jest tylko czÚĂciÈ URL (na przykïad 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 Dziaïanie przeciwne do encodeURI() i encodeURIComponent() majÈ funkcje decodeURI() i decode ´URIComponent(). W starszym kodzie moĝna natknÈÊ siÚ na starsze funkcje escape() i unescape(), jednak sÈ one przestarzaïe i nie naleĝy ich stosowaÊ. eval() Funkcja eval() pobiera ïañcuch znaków i uruchamia go jako kod w jÚzyku JavaScript: eval( var ii = 2; ) ii 2 eval( var ii = 2; ) dziaïa dokïadnie tak samo jako var ii = 2; 80 Rozdziaá 3. • Funkcje SÈ sytuacje, w których eval() siÚ przydaje, jednak w miarÚ moĝliwoĂci naleĝy tej funkcji uni- kaÊ. Z reguïy moĝna zastosowaÊ inne rozwiÈzania, które w wiÚkszoĂci przypadków sÈ bardziej eleganckie i ïatwiejsze w utrzymaniu. Weterani JavaScriptu jak mantrÚ powtarzajÈ zdanie „eval is evil” („eval to samo zïo”). Moĝna wymieniÊ nastÚpujÈce wady tej funkcji: Q WydajnoĂÊ: wykonywanie kodu „na ĝywo” jest wolniejsze od wykonywania kodu zapisanego w skrypcie. Q Bezpieczeñstwo: JavaScript ma duĝe moĝliwoĂci, co oznacza, ĝe przy jego „pomocy” moĝna coĂ zepsuÊ. JeĂli nie moĝesz ufaÊ ěródïu, z którego pochodzi wejĂcie przekazywane do eval(), nie wywoïuj tej funkcji. Bonus — funkcja alert() Spójrzmy jeszcze na bardzo popularnÈ funkcjÚ alert(). Nie naleĝy ona do rdzenia jÚzyka (nie ma jej w specyfikacji ECMA), ale moĝna z niej korzystaÊ w Ărodowisku przeglÈdarki. Pozwala ona na wyĂwietlanie komunikatów w okienku dialogowym. Czasami przydaje siÚ to podczas testowania i debugowania aplikacji, chociaĝ w tym celu lepiej korzystaÊ z debugera Firebug. Na poniĪszym rysunku widaü efekt wykonania kodu alert( halo! ). PamiÚtaj tylko, ĝe okienko dialogowe blokuje wÈtek przeglÈdarki, co oznacza, ĝe ĝaden inny kod nie zostanie wykonany, zanim uĝytkownik nie kliknie OK. JeĂli aplikacja jest czÚsto aktu- alizowanÈ aplikacjÈ AJAX, to alert() nie jest najlepszym pomysïem. ZasiÚg zmiennych Warto zwróciÊ uwagÚ, zwïaszcza, jeĂli jest siÚ osobÈ, która wczeĂniej programowaïa w innym jÚzyku, ĝe zmienne w jÚzyku JavaScript nie sÈ definiowane w obrÚbie bloku, tylko funkcji. Oznacza to, ĝe jeĂli zmienna zostaïa zdefiniowana wewnÈtrz funkcji, nie jest widoczna poza niÈ. Natomiast zmienna zdefiniowana wewnÈtrz bloku if lub for jest widoczna poza blokiem. Zmienne globalne to zmienne uĝywane poza funkcjami, natomiast zmienne lokalne to zmienne uĝywane wewnÈtrz funkcji. Kod wewnÈtrz funkcji ma dostÚp zarówno do zmiennych global- nych, jak i do swoich zmiennych lokalnych. 81 JavaScript. Programowanie obiektowe W poniĝszym przykïadzie: Q funkcja f() ma dostÚp 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 naleĝy mieÊ na uwadze, ĝe jeĂli do deklaracji zmiennej nie zostanie uĝyta instrukcja var, zmienna bÚdzie miaïa zasiÚg globalny. Spójrzmy na przykïad: Co siÚ staïo? Funkcja f() zawiera zmiennÈ local. Przed wywoïaniem funkcji zmienna nie ist- nieje. Jednak podczas pierwszego wywoïania funkcji zmienna jest tworzona i ma zasiÚg glo- balny. Dlatego jeĂli wówczas spróbujemy siÚgnÈÊ do zmiennej local, okaĝe siÚ ona dostÚpna. Dobre rady  Staraj siÚ ograniczaÊ liczbÚ zmiennych globalnych. Wyobraě sobie dwie osoby pracujÈce nad dwiema róĝnymi funkcjami w tym samym skrypcie, które przypadkowo postanawiajÈ nadaÊ tÚ samÈ nazwÚ zmiennej globalnej. Moĝe to doprowadziÊ do nieoczekiwanych wyników i trudnych do wykrycia bïÚdów.  Zawsze deklaruj zmienne za pomocÈ instrukcji var. 82 Poniĝszy przykïad ilustruje waĝny aspekt podziaïu na zmienne lokalne i globalne. Rozdziaá 3. • Funkcje var a = 123; function f() { alert(a); var a = 1; alert(a); } f(); ByÊ moĝe spodziewasz siÚ, ĝe pierwszy alert() wyĂwietli 123 (wartoĂÊ globalnej zmiennej a), a drugi wyĂwietli 1 (wartoĂÊ lokalnej zmiennej a). Jednak stanie siÚ inaczej. Pierwszy alert() pokaĝe undefined . Stanie siÚ tak dlatego, ĝe wewnÈtrz funkcji zasiÚg lokalny jest waĝniejszy od globalnego. Zmienna lokalna nadpisuje zmiennÈ globalnÈ o tej samej nazwie. Podczas wy- konywania pierwszego alert(), a nie byïo jeszcze zdefiniowane (stÈd wartoĂÊ undefined), ale juĝ istniaïo w lokalnej przestrzeni nazw. Funkcje sÈ danymi Zrozumienie tego punktu widzenia bÚdzie na póěniejszym etapie bardzo waĝne — funkcje tak naprawdÚ sÈ danymi. Oznacza to, ĝe nastÚpujÈce dwie metody definiowania funkcji sÈ równowaĝne: function f(){return 1;} var f = function(){return 1;} Drugi z pokazanych sposobów definiowania funkcji okreĂla siÚ mianem zapisu literaïowego funkcji. JeĂli na zmiennej, której zostaïa przypisana wartoĂÊ bÚdÈca funkcjÈ, wywoïamy ope- rator typeof, zwróci on ïañcuch znaków function . function f(){return 1;} typeof f function Zatem: funkcje w jÚzyku JavaScript sÈ specjalnym typem danych. PosiadajÈ dwie istotne cechy: Q zawierajÈ kod, Q sÈ wykonywalne (mogÈ byÊ wywoïywane). Wiesz juĝ, ĝe funkcje wywoïuje siÚ poprzez podanie nawiasu po ich nazwie. NastÚpny przy- kïad pokazuje, ĝe ta metoda zadziaïa niezaleĝnie od sposobu definicji funkcji. WidaÊ w nim takĝe, ĝe funkcja jest traktowana jak normalna wartoĂÊ, którÈ moĝna przypisaÊ nowej zmien- nej lub nawet wykasowaÊ. var sum = function(a, b) {return a + b;} var add = sum; delete sum true 83 JavaScript. Programowanie obiektowe 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 moĝe zaczynaÊ siÚ liczbÈ i moĝe zawieraÊ dowolnÈ kombinacjÚ liter, cyfr oraz znaku podkreĂlnika. Funkcje anonimowe JavaScript pozwala na rozrzucanie fragmentów danych po caïym programie. Wyobraě sobie, ĝe Twój program zawiera nastÚpujÈcy fragment kodu: test ; [1,2,3]; undefined; null; 1; Kod wyglÈda doĂÊ dziwnie, poniewaĝ nie robi nic poĝytecznego, jednak jest poprawny i nie spowoduje bïÚdu. Moĝna powiedzieÊ, ĝe zawiera dane anonimowe, czyli nieprzypisane do ĝadnej zmiennej i nieposiadajÈce nazwy. Wiesz juĝ, ĝe funkcje moĝna traktowaÊ jak wszystkie inne dane. W zwiÈzku z tym ich takĝe moĝna uĝywaÊ 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È moĝna przekazaÊ jako parametr do innej funkcji. Funkcja odbierajÈca ten parametr moĝe przeprowadziÊ operacje na otrzymanej funkcji. Q Funkcje anonimowe moĝne definiowaÊ i od razu uruchamiaÊ. Przyjrzyjmy siÚ uwaĝniej obu zastosowaniom funkcji anonimowych. Wywoïania zwrotne Skoro funkcje to dane, które moĝna przypisaÊ zmiennym, to moĝna je definiowaÊ, kasowaÊ, kopiowaÊ… Dlaczego zatem nie miaïoby byÊ moĝliwe przekazywanie ich jako parametrów do innych funkcji? 84 Rozdziaá 3. • Funkcje Oto przykïad funkcji, która pobiera dwie funkcje jako parametry, wywoïuje je, po czym zwra- ca wynik bÚdÈcy sumÈ zwróconych przez nie wartoĂci: function wywolaj_i_dodaj(a, b){ return a() + b(); } Zdefiniujmy teraz dwie pomocnicze funkcje, które bÚdÈ zwracaïy ustalone wartoĂci: function jeden() { return 1; } function dwa() { return 2; } Moĝemy przekazaÊ je oryginalnej funkcji i obejrzeÊ wynik: wywolaj_i_dodaj(jeden, dwa); 3 Jako parametry moĝna takĝe przekazywaÊ funkcje anonimowe. Wówczas zamiast definiowania jeden() i dwa() wystarczyïoby napisaÊ: wywolaj_i_dodaj(function(){return 1;}, function(){return 2;}) JeĂli funkcja A zostaje przekazana funkcji B i B wywoïuje A, czÚsto mówi siÚ, ĝe A jest wy- woïaniem zwrotnym (ang. callback function). JeĂli A nie ma nazwy, to jest anonimowym wy- woïaniem zwrotnym. Jakie zastosowania majÈ takie funkcje? Spójrzmy na przykïady, które ilustrujÈ nastÚpujÈce zalety wywoïañ zwrotnych: Q Moĝna przekazywaÊ funkcje bez koniecznoĂci ich nazywania, co oznacza, ĝe potrzebnych jest mniej zmiennych globalnych. Q JeĂli przeniesiemy obowiÈzek wywoïania funkcji na innÈ funkcjÚ, nasz kod bÚdzie krótszy. Q Wywoïania zwrotne mogÈ korzystnie wpïynÈÊ na wydajnoĂÊ aplikacji. Przykïady wywoïañ zwrotnych Przeanalizujmy czÚsty scenariusz: mamy funkcjÚ, która zwraca wartoĂÊ, przekazywanÈ na- stÚpnie kolejnej funkcji. W naszym przykïadzie pierwsza funkcja, pomnozRazyDwa(), przyjmuje trzy parametry, przechodzi przez nie w pÚtli oraz zwraca tablicÚ zawierajÈcÈ wynik. Druga funkcja, dodajJeden(), pobiera wartoĂÊ, dodaje do niej jeden, po czym zwraca wynik. 85 JavaScript. Programowanie obiektowe 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 zawieraïa trzy elementy, z których kaĝdy przejdzie przez obie funkcje. Zacznijmy od pomnozRazyDwa(). var myarr = []; myarr = pomnozRazyDwa(10, 20, 30); [20, 40, 60] Moĝemy teraz wywoïywaÊ funkcjÚ dodajJeden() w pÚtli, raz dla kaĝdego elementu tablicy: for (var i = 0; i 3; i++) {myarr[i] = addOne(myarr[i]);} myarr [21, 41, 61] Wszystko zadziaïa, ale jest tu pole do poprawek. Po pierwsze, przykïad uruchamia dwie pÚtle, które mogÈ byÊ kosztowne, jeĂli powtórzeñ jest wiele. ¿Èdany wynik moĝna otrzymaÊ przy uĝyciu jednej tylko pÚtli. Oto, jak zmieniÊ funkcjÚ pomnozRazyDwa() tak, by jako parametr przyjmowaïa funkcjÚ i wywoïywaïa jÈ przy kaĝdej 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- woïania. Przekazuje siÚ do niego wartoĂci poczÈtkowe oraz funkcjÚ, która ma zostaÊ wywoïana na kaĝdej z tych wartoĂci. 86 Rozdziaá 3. • Funkcje myarr = pomnozRazyDwa(1, 2, 3, dodajJeden); [3, 5, 7] Zamiast definiowania funkcji dodajJeden() moĝna skorzystaÊ z funkcji anonimowej, dziÚki czemu zdefiniowana zostanie jedna zmienna globalna mniej. myarr = pomnozRazyDwa(1, 2, 3, function(a){return a + 1}); [3, 5, 7] OczywiĂcie tej samej funkcji moĝna jako parametr przekazaÊ róĝne funkcje anonimowe: myarr = multiplyByTwo(1, 2, 3, function(a){return a + 2}); [4, 6, 8] Funkcje samowywoïujÈce siÚ OmówiliĂmy juĝ funkcje anonimowe i wywoïania zwrotne. Przejděmy teraz do innego zastosowa- nia funkcji anonimowych — wywoïywania funkcji zaraz po ich zdefiniowaniu. Oto przykïad: ( function(){ alert( uuu! ); } )() PoczÈtkowo moĝe to wyglÈdaÊ groěnie, ale tak naprawdÚ to proste — funkcjÚ anonimowÈ umieszcza siÚ w nawiasie, po którym nastÚpuje inny nawias (w przykïadzie jest pusty). Drugi nawias oznacza „uruchom teraz”. To w nim umieszcza siÚ ewentualne parametry funkcji. ( function(imie){ alert( CzeĂÊ + imie + ! ); } )( stary ) JednÈ z zalet samowywoïujacych 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Ú wewnÈtrz pÚtli lub innej funkcji). Dlatego anonimowe funkcje samowywoïujÈce najlepiej nadajÈ siÚ do wykonywania jednokrotnych zadañ inicjalizacyjnych. Funkcje wewnÚtrzne (prywatne) Skoro funkcje sÈ zwykïymi wartoĂciami, nic nie stoi na przeszkodzie, by zdefiniowaÊ funkcjÚ wewnÈtrz innej funkcji. 87 JavaScript. Programowanie obiektowe function a(param) { function b(theinput) { return theinput * 2; }; return Wynik wynosi + b(param); }; StosujÈc drugÈ notacjÚ definiowania funkcji, moĝemy napisaÊ: var a = function(param) { var b = function(theinput) { return theinput * 2; }; return Wynik wynosi + b(param); }; Kiedy globalna funkcja a() zostanie wywoïana, wywoïa takĝe lokalnÈ funkcjÚ b(). Jako ĝe b() jest lokalna, nie jest dostÚpna 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 pïynÈ nastÚpujÈce korzyĂci: Q Nie dochodzi do zaĂmiecenia globalnej przestrzeni nazw (zmniejszone ryzyko kolizji nazw). Q PrywatnoĂÊ: na zewnÈtrz widoczne sÈ tylko te funkcje, które programista chce udostÚpniÊ. FunkcjonalnoĂci nieprzeznaczone dla reszty aplikacji sÈ ukryte. Funkcje, które zwracajÈ funkcje Wspominaïem juĝ, ĝe funkcja zawsze zwraca wartoĂÊ, a jeĂli nie robi tego w sposób jawny, to niejawnie zwracana jest wartoĂÊ undefined. Funkcja zwraca dokïadnie jednÈ wartoĂÊ, która z powodzeniem moĝe byÊ innÈ funkcjÈ. function a() { alert( A! ); return function(){ alert( B! ); }; } 88 Rozdziaá 3. • Funkcje Widoczna powyĝej funkcja a() wykonuje swojÈ pracÚ (mówi A! ) i zwraca innÈ funkcjÚ, która robi coĂ innego (mówi B! ). Wynik moĝna przypisaÊ jakiejĂ zmiennej i uĝywaÊ jej jako nor- malnej funkcji. var newFunc = a(); newFunc(); Pierwsza linia powyĝszego kodu spowoduje wyĂwietlenie okienka z wiadomoĂciÈ A! , a dru- ga — okienka z wiadomoĂciÈ B! . JeĂli funkcja zwracana przez innÈ funkcjÚ ma zostaÊ wykonana natychmiast, bez potrzeby przypisywania jej do nowej zmiennej, wystarczy dodaÊ jeszcze jeden nawias. Wynik koñcowy bÚdzie taki sam jak wczeĂniej. a()(); Funkcjo, przepiszĝe siÚ! Poniewaĝ funkcje potrafiÈ zwracaÊ funkcje, moĝliwe jest zastÈpienie oryginalnej funkcji tÈ zwracanÈ. WróÊmy do poprzedniego przykïadu. WartoĂÊ zwróconÈ przez wywoïanie a() moĝna przypisaÊ zmiennej a, nadpisujÈc w ten sposób istniejÈcÈ funkcjÚ: a = a(); Powyĝsza linia przy pierwszym uruchomieniu spowoduje wyĂwietlenie A! , jednak jej dru- gie uruchomienie wyĂwietli B! . Opisany mechanizm jest przydatny, jeĂli funkcja wykonuje pewne jednorazowe zadanie. Po zakoñczeniu zadania zmiennej przechowujÈcej funkcjÚ przypisywana jest nowa wartoĂÊ, dziÚki czemu operacje nie muszÈ byÊ powtarzane za kaĝdym razem, gdy ktoĂ wywoïa funkcjÚ. W ostatnim przykáadzie funkcja zostaáa przedefiniowana z zewnątrz — pobraliĞmy zwróconą wartoĞü i przypisaliĞmy ją funkcji. JednakĪe moĪliwe jest równieĪ przepisanie funkcji od Ğrodka. function a() { alert( A! ); a = function(){ alert( B! ); }; } Przy pierwszym wywoïaniu funkcja: Q WyĂwietli A! (zaïóĝmy, ĝe to wïaĂnie jest nasze jednorazowe zadanie inicjalizacyjne). Q Zmieni definicjÚ globalnej zmiennej a, przypisujÈc jej nowÈ funkcjÚ. Kaĝde kolejne wywoïanie bÚdzie powodowaïo wyĂwietlenie B! . 89 JavaScript. Programowanie obiektowe Oto inny przykáad, 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 przykïadzie: Q Mamy funkcje prywatne: inicjalizacja() i normalnaPraca(). Q Mamy funkcjÚ samowywoïujÈcÈ siÚ: funkcja a() jest wywoïywana dziÚki nawiasowi po jej definicji. Q Pierwsze wywoïanie a() polega na wywoïaniu funkcji inicjalizacja() i zwróceniu referencji do zmiennej normalnaPraca, która jest funkcjÈ. ZwróÊ uwagÚ na brak nawiasów przy zwracanej wartoĂci — nie ma ich dlatego, ĝe zwracamy do funkcji referencjÚ, a nie wynik wywoïania tejĝe funkcji. Q Jako ĝe kod zaczyna siÚ od var a =, wartoĂÊ zwrócona przez samowywoïujÈcÈ siÚ funkcjÚ zostanie przypisana zmiennej a. JeĂli chcesz sprawdziÊ, czy poprawnie rozumiesz omówiony zakres materiaïu, spróbuj odpo- wiedzieÊ na poniĝsze pytania. Jakie bÚdzie zachowanie napisanego przed chwilÈ programu, gdy: Q zostanie wgrany po raz pierwszy? Q po wgraniu zostanie wywoïane a()? Przedstawione mechanizmy okazujÈ siÚ bardzo przydatne w Ărodowisku przeglÈdarki. Róĝne przeglÈdarki mogÈ realizowaÊ konkretne zadania na róĝne sposoby. Przy zaïoĝeniu, ĝe wïaĂci- woĂci przeglÈdarki nie zmieniÈ siÚ pomiÚdzy wywoïaniami funkcji, moĝemy stworzyÊ funkcjÚ, która wybierze sposób dziaïania najlepiej dopasowany do danej przeglÈdarki, po czym w od- powiedni sposób zmieni swojÈ definicjÚ, dziÚki czemu tylko raz bÚdzie musiaïa wykrywaÊ typ przeglÈdarki. Konkretne przykïady zastosowania tego scenariusza bÚdzie moĝna zobaczyÊ na dalszych stronach ksiÈĝki. DomkniÚcia Pozostaïa czÚĂÊ tego rozdziaïu jest poĂwiÚcona domkniÚciom (czyĝ istnieje lepszy sposób na zamkniÚcie rozdziaïu?). DomkniÚcia poczÈtkowo mogÈ wydawaÊ siÚ trudne do zrozumienia, dlatego nie zniechÚcaj siÚ, jeĂli nie pojmiesz wszystkiego od razu. Postaraj siÚ doczytaÊ rozdziaï 90 Rozdziaá 3. • Funkcje do koñca i poeksperymentowaÊ z przykïadami, a jeĂli niektóre zagadnienia nadal nie bÚdÈ ja- sne, moĝesz do nich wróciÊ póěniej, kiedy inne mechanizmy omówione w tym rozdziale nie bÚdÈ juĝ sprawiaïy Ci ĝadnego kïopotu. Zanim zajmiemy siÚ domkniÚciami, powtórzmy i rozszerzmy trochÚ pojÚcia zakresu w jÚzyku JavaScript. ’añcuch zakresów Jak juĝ Ci wiadomo, JavaScript nie wyróĝnia ĝadnych zakresów ograniczonych nawiasami klamrowymi, ale istnieje zakres funkcji. Zmienna zdefiniowana wewnÈtrz funkcji nie jest wi- doczna poza tÈ funkcjÈ, natomiast zmienna zdefiniowana wewnÈtrz bloku kodu (np. po if lub w pÚtli for) jest dostÚpna poza blokiem. var a = 1; function f(){var b = 1; return a;} f(); 1 b b is not defined Zmienna a naleĝy do globalnej przestrzeni nazw, podczas gdy zmienna b tylko do zakresu funkcji f(). Dlatego: Q WewnÈtrz f() widoczne sÈ zarówno a i b. Q WewnÈtrz f() widoczna jest zmienna a, ale nie zmienna b. JeĂli zdefiniujesz funkcjÚ n() osadzonÈ w f(), n() bÚdzie miaïa dostÚp do zmiennych ze swo- jego zakresu, a takĝe do zmiennych swoich „rodziców”. W takim wypadku mówimy o ïañcuchu zakresów, który moĝe byÊ dowolnie dïugi (gïÚboki). var a = 1; function f(){ var b = 1; function n() { var c = 3; } } ZasiÚg leksykalny Funkcje w jÚzyku JavaScript majÈ zasiÚg leksykalny. Oznacza to, ĝe funkcje tworzÈ swoje wïa- sne Ărodowisko (zakres) podczas definicji, a nie podczas wywoïania. Spójrzmy na przykïad: 91 JavaScript. Programowanie obiektowe function f1(){var a = 1; f2();} function f2(){return a;} f1(); a is not defined WewnÈtrz funkcji f1() wywoïujemy funkcjÚ f2(). Poniewaĝ zmienna lokalna a znajduje siÚ takĝe wewnÈtrz f1(), ktoĂ mógïby siÚ spodziewaÊ, ĝe f2() bÚdzie miaïa dostÚp do a, jednak tak nie jest. W momencie definicji f2() (a nie w momencie wywoïania) nigdzie nie byïo Ăladu a. f2(), podobnie jak f1(), ma dostÚp jedynie do wïasnego zakresu oraz do zakresu globalnego. f1() i f2() nie wspóïdzielÈ zakresów lokalnych. Podczas definiowania funkcja zapamiÚtuje swoje Ărodowisko, to znaczy swój ïañcuch zakresów. Nie znaczy to wcale, ĝe funkcja pamiÚta kaĝdÈ konkretnÈ zmiennÈ, która pojawiïa siÚ w tym zakresie. WrÚcz przeciwnie — zmienne moĝna dodawaÊ, usuwaÊ i uaktualniaÊ, a funkcja zawsze bÚdzie widziaïa najnowszy, aktualny stan zmiennych. JeĂli rozszerzymy przykïad o deklaracjÚ globalnej zmiennej a, stanie siÚ ona widoczna dla f2(), poniewaĝ f2() zna ĂcieĝkÚ do zmiennych globalnych i ma dostÚp do caïoĂci tego Ărodowiska. ZwróÊ uwagÚ na to, ĝe f1() zawiera wywo- ïanie f2(), które dziaïa — mimo ĝe f2() nie zostaïa jeszcze zdefiniowana. f1() musi tylko po- siadaÊ wiedzÚ o wïasnym zakresie, by wszystko, co siÚ w nim pojawi, stawaïo siÚ automatycznie dostÚpne 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 — moĝna dodawaÊ zmienne, usuwaÊ je, a potem dodawaÊ je ponownie. Moĝesz poeksperymentowaÊ, kasujÈc funkcjÚ f2(), a potem definiujÈc jÈ ponownie, ale z innym ciaïem. Funkcja f1() nadal bÚdzie dziaïaÊ, poniewaĝ musi znaÊ jedynie sposób dostÚpu do swojego zakresu — nie jest jej po- trzebna wiedza o tym, co kiedyĂ do tego zakresu naleĝaïo. CiÈg dalszy przykïadu: 92 Rozdziaá 3. • Funkcje true f1() f2 is not defined var f2 = function(){return a * 2;} var a = 5; 5 f1(); 10 Przerwanie ïañcucha za pomocÈ domkniÚcia Zaczniemy od ilustracji. Poniĝej widzisz zakres globalny. Wyobraě go sobie jako wszechĂwiat. Moĝe on zawieraÊ zmienne, takie jak a, i funkcje, jak F. Funkcje posiadajÈ wïasnÈ przestrzeñ, którÈ mogÈ wykorzystywaÊ do przechowywania innych zmiennych (i funkcji). W pewnym momencie rysunek bÚdzie wyglÈdaï mniej wiÚcej tak: 93 JavaScript. Programowanie obiektowe JeĂli jesteĂ w punkcie a, jesteĂ w przestrzeni globalnej. JeĂli w punkcie b, który naleĝy do przestrzeni funkcji F, masz dostÚp do przestrzeni globalnej oraz do przestrzeni F. JeĂli znala- zïeĂ siÚ w punkcie c, który naleĝy do funkcji N, moĝesz siÚgnÈÊ do przestrzeni globalnej, prze- strzeni F oraz N. Nie da siÚ siÚgnÈÊ z a do b, poniewaĝ punkt b nie jest widoczny poza F. Moĝesz natomiast uzyskaÊ dostÚp z c do b lub z N do b. Ciekawe rzeczy (domkniÚcie) 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 pamiÚtajÈ Ărodowisko, w którym zostaïy zdefiniowane, N nadal ma dostÚp do przestrzeni F, a co za tym idzie dostÚp do b. Jest to ciekawe dlatego, ĝe N znajduje siÚ tam gdzie a, a jednak N ma dostÚp do b, zaĂ a nie. Jak N udaje siÚ przerwaÊ ïañcuch? IstniejÈ dwa sposoby: N moĝe zostaÊ zmiennÈ globalnÈ (pominiÚcie var) lub moĝe zostaÊ zwrócona przez F do przestrzeni globalnej. Zobaczmy, jak to wyglÈda w praktyce. DomkniÚcie 1. Przyjrzyj siÚ uwaĝnie tej funkcji: function f(){ var b = b ; return function(){ return b; } } 94 Rozdziaá 3. • Funkcje Funkcja zawiera lokalnÈ zmiennÈ b, która nie jest dostÚpna z przestrzeni globalnej: b b is not defined ZwróÊ uwagÚ na wartoĂÊ zwracanÈ przez f(): jest ona innÈ funkcjÈ. Moĝesz o niej myĂleÊ jako o N z przedstawionych powyĝej rysunków. Nowa funkcja ma dostÚp do swojej przestrzeni prywatnej, do przestrzeni funkcji f() oraz do przestrzeni globalnej. Widzi zatem równieĝ b. Poniewaĝ f() moĝna wywoïaÊ w przestrzeni globalnej (jest funkcjÈ globalnÈ), moĝesz jÈ wy- woïaÊ i przypisaÊ zwracanÈ przez niÈ wartoĂÊ innej zmiennej globalnej. Wynikiem bÚdzie nowa funkcja globalna, która ma dostÚp do prywatnej przestrzeni f(). var n = f(); n(); b DomkniÚcie 2. Przykïad, który nastÈpi za chwilÚ, pozwala uzyskaÊ ten sam wynik co przykïad wczeĂniejszy, jednak z zastosowaniem nieco innych metod. Funkcja f() nie bÚdzie zwracaïa funkcji, a za- miast tego utworzy nowÈ, globalnÈ funkcjÚ n() wewnÈtrz swojego ciaïa. Zacznijmy od deklaracji zmiennej, do której póěniej przypiszemy nowÈ funkcjÚ. Nie jest to obowiÈzkowe, ale zawsze warto deklarowaÊ zmienne. Definicja funkcji f() moĝe wyglÈdaÊ tak: var n; function f(){ var b = b ; n = function(){ return b; } } Co siÚ stanie po wywoïaniu f()? f(); WewnÈtrz przestrzeni f() definiowana jest nowa funkcja. Poniewaĝ nie zostaïa uĝyta instruk- cja var, funkcja jest globalna. W czasie definicji funkcja n() znajdowaïa siÚ wewnÈtrz f(), zatem ma dostÚp do zakresu zmiennych f(). n() zachowa prawo dostÚpu nawet wtedy, gdy stanie siÚ czÚĂciÈ przestrzeni globalnej. n(); b 95 JavaScript. Programowanie obiektowe DomkniÚcie 3. i jedna definicja W oparciu o to, co zostaïo powiedziane do tej pory, moĝemy powiedzieÊ, ĝe domkniÚcie jest tworzone, gdy funkcja zachowuje dostÚp do zakresu rodzica po tym, jak rodzic zwróciï jÈ do globalnej przestrzeni nazw. Argument przekazany funkcji wewnÈtrz niej jest dostÚpny jako zmienna globalna. Moĝesz stwo- rzyÊ funkcjÚ zwracajÈcÈ innÈ funkcjÚ, która z kolei zwraca argument przekazany rodzicowi. function f(arg) { var n = function(){ return arg; }; arg++; return n; } FunkcjÚ moĝna wywoïaÊ w nastÚpujÈcy sposób: var m = f(123); m(); 124 Zauwaĝ, ĝe zmienna arg zostaïa zwiÚkszona juĝ po definicji funkcji, a pomimo tego m() zwróciïa aktualnÈ wartoĂÊ. Jest to kolejny dowód na to, ĝe funkcje sÈ zwiÈzane ze swoimi zakresami, a nie z przechowywanymi tam w danym momencie zmiennymi i ich wartoĂciami. DomkniÚcia w pÚtli PokaĝÚ teraz coĂ, co czÚsto prowadzi do bardzo trudnych do wykrycia bïÚdów, poniewaĝ na pierwszy rzut oka wydaje siÚ, ĝe nie ma tam miejsca na pomyïkÚ. Napiszmy pÚtlÚ o trzech iteracjach, która za kaĝdym przebiegiem zwraca numer pÚtli. 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; } 96 Rozdziaá 3. • Funkcje Wywoïajmy jÈ teraz, przypisujÈc wynikowÈ tablicÚ zmiennej a. var a = f(); Mamy zatem tablicÚ z trzema funkcjami. Wywoïajmy je, podajÈc nawiasy po kaĝdym 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, niezupeïnie to mieliĂmy na myĂli. Co siÚ staïo? UtworzyliĂmy trzy domkniÚcia, które wskazujÈ na tÚ samÈ lokalnÈ zmiennÈ i. DomkniÚcia nie pamiÚtajÈ wartoĂci, tylko przechowujÈ referencjÚ do zmiennej i — dlatego zwracajÈ jej aktualnÈ wartoĂÊ. Po wyjĂciu z pÚtli wartoĂciÈ zmiennej i jest 3. Wszystkie funkcje wskazujÈ na tÚ samÈ wartoĂÊ. (Dla lepszego zrozumienia pÚtli zastanów siÚ, dlaczego wartoĂciÈ i jest 3, a nie 2). Jak zatem zaimplementowaÊ poprawne zachowanie? Potrzebne nam sÈ trzy róĝne zmienne. Eleganckie rozwiÈzanie polega na wykorzystaniu kolejnego domkniÚcia: 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 97 JavaScript. Programowanie obiektowe a[2](); 2 W tej wersji nie tworzymy funkcji zwracajÈcej i, tylko przekazujemy i innej, samowywoïujÈ- cej siÚ funkcji. W tej funkcji i staje siÚ lokalnÈ zmiennÈ x i za kaĝdym razem ma innÈ wartoĂÊ. Ten sam wynik moĝna uzyskaÊ przy uĝyciu „normalnej” (czyli niesamowywoïujÈcej siÚ) funkcji wewnÚtrznej. Kluczem do sukcesu jest wykorzystanie Ărodkowej funkcji do ustalenia wartoĂci 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 dostÚpowe ChcÚ opowiedzieÊ o jeszcze dwóch sposobach wykorzystania domkniÚÊ. Pierwszy z nich po- lega na utworzeniu funkcji dostÚpowych get (pobranie wartoĂci) i set (ustawienie wartoĂci). Zaïóĝmy, ĝe posiadasz zmiennÈ, która moĝe przyjmowaÊ wartoĂci tylko ze ĂciĂle okreĂlonego zbioru. Nie chcesz odkrywaÊ tej zmiennej, poniewaĝ chcesz zabezpieczyÊ siÚ przed sytuacjÈ, w której pewien fragment kodu nada jej niedozwolonÈ wartoĂÊ. RozwiÈzaniem jest utworze- nie schronienia dla tej zmiennej wewnÈtrz pewnej funkcji i stworzenie dwóch dodatkowych funkcji, które bÚdÈ odczytywaïy i ustawiaïy jej wartoĂÊ. Funkcja ustawiajÈca wartoĂÊ moĝe zawieraÊ pewnÈ logikÚ, która nie pozwoli na nadanie zmiennej wartoĂci spoza dozwolonego zbioru (jednak dla uproszczenia przykïadu pomiñmy walidacjÚ). Funkcje dostÚpowe powinny znaleěÊ siÚ wewnÈtrz tej samej funkcji, która zawiera tajnÈ zmiennÈ, tak by dzieliïy ten sam zakres: var getValue, setValue; (function() { var secret = 0; getValue = function(){ return secret; }; 98 Rozdziaá 3. • Funkcje setValue = function(v){ secret = v; }; })() Funkcja, która opakowuje zmiennÈ i dwie funkcje dostÚpowe, jest tutaj samowywoïujÈcÈ siÚ funkcjÈ anonimowÈ. Definiuje ona setValue() i getValue() jako funkcje globalne, podczas gdy zmienna secret pozostaje lokalna i nie jest dostÚpna bezpoĂrednio. getValue() 0 setValue(123) getValue() 123 Iterator Ostatni przykïad domkniÚcia (a zarazem ostatni przykïad w tym rozdziale) pokazuje wykorzy- stanie domkniÚÊ w celu osiÈgniÚcia funkcjonalnoĂci iteratora. Wiesz juĝ, jak wykorzystaÊ pÚtlÚ do przejĂcia przez wszystkie elementy zwykïej tablicy. Mo- ĝesz jednak napotkaÊ bardziej zïoĝonÈ strukturÚ danych, w której kolejnoĂÊ elementów jest okreĂlana przez bardziej zïoĝony zestaw reguï. Wówczas skomplikowanÈ logikÚ rozwiÈzujÈcÈ problem „kto nastÚpny?” umieszczasz w wygodnej w uĝyciu funkcji next(). NastÚpnie wywoïu- jesz next() za kaĝdym razem, gdy chcesz pobraÊ kolejnÈ wartoĂÊ. Na potrzeby przykïadu wy- korzystamy jednak zwykïÈ tablicÚ, a nie zïoĝonÈ strukturÚ danych. Oto funkcja inicjalizacyjna, która pobiera tablicÚ, a takĝe definiuje prywatny wskaěnik i, zaw- sze wskazujÈcy nastÚpny element w tablicy: function setup(x) { var i = 0; return function(){ return x[i++]; }; } Wywoïanie funkcji setup() z parametrem bÚdÈcym tablicÈ danych spowoduje automatyczne utworzenie funkcji next(). var next = setup([ a , b , c ]); Dalej czekajÈ nas sam przyjemnoĂci: wywoïujÈc wciÈĝ tÚ samÈ funkcjÚ, przejdziemy przez wszystkie elementy tablicy. 99 JavaScript. Programowanie obiektowe next(); a next(); b next(); c Podsumowanie WïaĂnie skoñczyliĂmy podstawowy kurs pojÚÊ zwiÈzanych z funkcjami. PrzejĂcie do konceptów programowania obiektowego oraz do wzorców wykorzystywanych w nowoczesnym progra- mowaniu w jÚzyku JavaScript powinno byÊ dla Ciebie proste. Do tej pory unikaliĂmy funkcjo- nalnoĂci obiektowych, ale od tej chwili nie bÚdziemy juĝ tego robiÊ. Powtórzmy materiaï przed- stawiony w tym rozdziale. Omówione zostaïy nastÚpujÈce kwestie: Q Definiowanie i wywoïywanie funkcji. Q Parametry funkcji i ich elastycznoĂÊ. Q Funkcje wbudowane: parseInt(), parseFloat(), isNaN(), isFinite(), eval(), a takĝe cztery funkcje do kodowania i dekodowania adresów URL. Q Zakres zmiennych: nie ma zakresu zwiÈzanego z nawiasami klamrowymi, istnieje zakres funkcji, funkcje majÈ zakres leksykalny, obowiÈzuje zasada ïañcucha zakresów. Q Funkcje to dane — funkcjÚ moĝna przypisaÊ zmiennej, z czego wynika szereg ciekawych zastosowañ, wĂród których moĝna wymieniÊ: Q prywatne funkcje i zmienne, funkcje anonimowe, Q Q wywoïania zwrotne, Q samowywoïujÈce siÚ funkcje, Q funkcje zmieniajÈce swojÈ definicjÚ. Q DomkniÚcia. mwiczenia 1. Napisz funkcjÚ, która przeksztaïca szesnastkowÈ definicjÚ koloru (np. niebieski to 0000FF ) na reprezentacjÚ RGB (np. rgb(0, 0, 255) ). Nazwij funkcjÚ getRGB() i przetestuj jÈ za pomocÈ nastÚpujÈcego kodu: 100 Rozdziaá 3. • Funkcje var a = getRGB( #00FF00 ); a; rgb(0, 255, 0) 2. Co pojawi siÚ w konsoli po uruchomieniu kaĝdej z poniĝszych 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 nastÚpujÈcego kodu? var a = 1; function f() { var a = 2; function n() { alert(a); } n(); } f(); powiedzieÊ dlaczego? 4.1 4. Wszystkie poniĝsze przykïady spowodujÈ wyĂwietlenie Uuu! . Czy potrafisz var f = alert; eval( f( Uuu! ) ); var e; var f = alert; eval( e=f )( Uuu! ); 4.2 4.3 ( function(){ return alert; } )()( Uuu! ); 101
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

JavaScript. Programowanie obiektowe
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: