informatyka javascript wzorce stoyan stefanov ebook

background image
background image

Tytuá oryginaáu: JavaScript Patterns

Táumaczenie: Rafaá JoĔca

ISBN: 978-83-246-3821-5

© Helion S.A. 2012.

Authorized Polish translation of the English edition of JavaScript Patterns ISBN 9780596806750 © 2010,
Yahoo!, Inc. All rights reserved.
This translation is published and sold by permission of O’Reilly Media, Inc., the owner of all rights to
publish and sell the same.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording or by any information storage retrieval system,
without permission from the Publisher.

Wszelkie prawa zastrzeĪone. Nieautoryzowane rozpowszechnianie caáoĞci lub fragmentu niniejszej
publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną,
fotograficzną, a takĪe kopiowanie ksiąĪki na noĞniku filmowym, magnetycznym lub innym powoduje
naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki wystĊpujące w tekĞcie są zastrzeĪonymi znakami firmowymi bądĨ towarowymi ich
wáaĞcicieli.

Autor oraz Wydawnictwo HELION doáoĪyli wszelkich staraĔ, by zawarte w tej ksiąĪce informacje byáy
kompletne i rzetelne. Nie biorą jednak Īadnej odpowiedzialnoĞci ani za ich wykorzystanie, ani za związane
z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie
ponoszą równieĪ Īadnej odpowiedzialnoĞci za ewentualne szkody wynikáe z wykorzystania informacji
zawartych w ksiąĪce.

Wydawnictwo HELION
ul. KoĞciuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (ksiĊgarnia internetowa, katalog ksiąĪek)

Drogi Czytelniku!
JeĪeli chcesz oceniü tĊ ksiąĪkĊ, zajrzyj pod adres
http://helion.pl/user/opinie/jascwz
MoĪesz tam wpisaü swoje uwagi, spostrzeĪenia, recenzjĊ.

Printed in Poland.

Kup książkę

Poleć książkę

Oceń książkę

Księgarnia internetowa

Lubię to! » Nasza społeczność

background image

5

Spis tre"ci

Wst#p ............................................................................................................................11

1. Wprowadzenie ............................................................................................................ 15

Wzorce

15

JavaScript — podstawowe cechy

16

Zorientowany obiektowo

16

Brak klas

17

Prototypy

18

"rodowisko

18

ECMAScript 5

18

Narz#dzie JSLint

19

Konsola

20

2. Podstawy ..................................................................................................................... 21

Tworzenie kodu $atwego w konserwacji

21

Minimalizacja liczby zmiennych globalnych

22

Problem ze zmiennymi globalnymi

22

Efekty uboczne pomini#cia var

24

Dost#p do obiektu globalnego

25

Wzorzec pojedynczego var

25

Przenoszenie deklaracji — problem rozrzuconych deklaracji var

26

P#tle for

27

P#tle for-in

29

Modyfikacja wbudowanych prototypów

31

Wzorzec konstrukcji switch

31

Unikanie niejawnego rzutowania

32

Unikanie eval()

32

Konwertowanie liczb funkcj% parseInt()

34

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

6

Spis tre"ci

Konwencje dotycz%ce kodu

34

Wci#cia

35

Nawiasy klamrowe

35

Po$o&enie nawiasu otwieraj%cego

36

Bia$e spacje

37

Konwencje nazewnictwa

38

Konstruktory pisane od wielkiej litery

38

Oddzielanie wyrazów

39

Inne wzorce nazewnictwa

39

Pisanie komentarzy

40

Pisanie dokumentacji interfejsów programistycznych

41

Przyk$ad dokumentacji YUIDoc

42

Pisanie w sposób u$atwiaj%cy czytanie

44

Ocenianie kodu przez innych cz$onków zespo$u

45

Minifikowanie kodu tylko w systemie produkcyjnym

46

Uruchamiaj narz#dzie JSLint

47

Podsumowanie

47

3. Litera$y i konstruktory .................................................................................................49

Litera$ obiektu

49

Sk$adnia litera$u obiektowego

50

Obiekty z konstruktora

51

Pu$apka konstruktora Object

51

W$asne funkcje konstruuj%ce

52

Warto'* zwracana przez konstruktor

53

Wzorce wymuszania u&ycia new

54

Konwencja nazewnictwa

54

U&ycie that

54

Samowywo$uj%cy si# konstruktor

55

Litera$ tablicy

56

Sk$adnia litera$u tablicy

56

Pu$apka konstruktora Array

56

Sprawdzanie, czy obiekt jest tablic%

57

JSON

58

Korzystanie z formatu JSON

58

Litera$ wyra&enia regularnego

59

Sk$adnia litera$owego wyra&enia regularnego

60

Otoczki typów prostych

61

Obiekty b$#dów

62

Podsumowanie

63

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Spis tre"ci

7

4. Funkcje .........................................................................................................................65

Informacje ogólne

65

Stosowana terminologia

66

Deklaracje kontra wyra&enia — nazwy i przenoszenie na pocz%tek

67

W$a'ciwo'* name funkcji

68

Przenoszenie deklaracji funkcji

68

Wzorzec wywo$ania zwrotnego

70

Przyk$ad wywo$ania zwrotnego

70

Wywo$ania zwrotne a zakres zmiennych

72

Funkcje obs$ugi zdarze+ asynchronicznych

73

Funkcje czasowe

73

Wywo$ania zwrotne w bibliotekach

74

Zwracanie funkcji

74

Samodefiniuj%ce si# funkcje

75

Funkcje natychmiastowe

76

Parametry funkcji natychmiastowych

77

Warto'ci zwracane przez funkcje natychmiastowe

77

Zalety i zastosowanie

79

Natychmiastowa inicjalizacja obiektu

79

Usuwanie warunkowych wersji kodu

80

W$a'ciwo'ci funkcji — wzorzec zapami#tywania

82

Obiekty konfiguracyjne

83

Rozwijanie funkcji

84

Aplikacja funkcji

84

Aplikacja cz#'ciowa

85

Rozwijanie funkcji

87

Kiedy u&ywa* aplikacji cz#'ciowej

89

Podsumowanie

89

5. Wzorce tworzenia obiektów ...................................................................................... 91

Wzorzec przestrzeni nazw

91

Funkcja przestrzeni nazw ogólnego stosowania

92

Deklarowanie zale&no'ci

94

Metody i w$a'ciwo'ci prywatne

95

Sk$adowe prywatne

96

Metody uprzywilejowane

96

Problemy z prywatno'ci%

96

Litera$y obiektów a prywatno'*

98

Prototypy a prywatno'*

98

Udost#pnianie funkcji prywatnych jako metod publicznych

99

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

8

Spis tre"ci

Wzorzec modu$u

100

Odkrywczy wzorzec modu$u

102

Modu$y, które tworz% konstruktory

102

Import zmiennych globalnych do modu$u

103

Wzorzec piaskownicy

103

Globalny konstruktor

104

Dodawanie modu$ów

105

Implementacja konstruktora

106

Sk$adowe statyczne

107

Publiczne sk$adowe statyczne

107

Prywatne sk$adowe statyczne

109

Sta$e obiektów

110

Wzorzec $a+cucha wywo$a+

112

Wady i zalety wzorca $a+cucha wywo$a+

112

Metoda method()

113

Podsumowanie

114

6. Wzorce wielokrotnego u%ycia kodu ..........................................................................115

Klasyczne i nowoczesne wzorce dziedziczenia

115

Oczekiwane wyniki w przypadku stosowania wzorca klasycznego

116

Pierwszy wzorzec klasyczny — wzorzec domy'lny

117

Pod%&anie wzd$u& $a+cucha prototypów

117

Wady wzorca numer jeden

119

Drugi wzorzec klasyczny — po&yczanie konstruktora

119

.a+cuch prototypów

120

Dziedziczenie wielobazowe przy u&yciu po&yczania konstruktorów

121

Zalety i wady wzorca po&yczania konstruktora

122

Trzeci wzorzec klasyczny — po&yczanie i ustawianie prototypu

122

Czwarty wzorzec klasyczny — wspó$dzielenie prototypu

123

Pi%ty wzorzec klasyczny — konstruktor tymczasowy

124

Zapami#tywanie klasy nadrz#dnej

125

Czyszczenie referencji na konstruktor

125

Podej'cie klasowe

126

Dziedziczenie prototypowe

129

Dyskusja

129

Dodatki do standardu ECMAScript 5

130

Dziedziczenie przez kopiowanie w$a'ciwo'ci

131

Wzorzec wmieszania

132

Po&yczanie metod

133

Przyk$ad — po&yczenie metody od obiektu Array

134

Po&yczenie i przypisanie

134

Metoda Function.prototype.bind()

135

Podsumowanie

136

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Spis tre"ci

9

7. Wzorce projektowe ....................................................................................................137

Singleton

137

U&ycie s$owa kluczowego new

138

Instancja we w$a'ciwo'ci statycznej

139

Instancja w domkni#ciu

139

Fabryka

141

Wbudowane fabryki obiektów

143

Iterator

143

Dekorator

145

Sposób u&ycia

145

Implementacja

146

Implementacja wykorzystuj%ca list#

148

Strategia

149

Przyk$ad walidacji danych

150

Fasada

152

Po'rednik

153

Przyk$ad

153

Po'rednik jako pami#* podr#czna

159

Mediator

160

Przyk$ad mediatora

160

Obserwator

163

Pierwszy przyk$ad — subskrypcja magazynu

163

Drugi przyk$ad — gra w naciskanie klawiszy

166

Podsumowanie

169

8. DOM i wzorce dotycz&ce przegl&darek ..................................................................... 171

Podzia$ zada+

171

Skrypty wykorzystuj%ce DOM

172

Dost#p do DOM

173

Modyfikacja DOM

174

Zdarzenia

175

Obs$uga zdarze+

175

Delegacja zdarze+

177

D$ugo dzia$aj%ce skrypty

178

Funkcja setTimeout()

178

Skrypty obliczeniowe

179

Komunikacja z serwerem

179

Obiekt XMLHttpRequest

180

JSONP

181

Ramki i wywo$ania jako obrazy

184

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

10

Spis tre"ci

Serwowanie kodu JavaScript klientom

184

.%czenie skryptów

184

Minifikacja i kompresja

185

Nag$ówek Expires

185

Wykorzystanie CDN

186

Strategie wczytywania skryptów

186

Lokalizacja elementu <script>

187

Wysy$anie pliku HTML fragmentami

188

Dynamiczne elementy <script> zapewniaj%ce nieblokuj%ce pobieranie

189

Wczytywanie leniwe

190

Wczytywanie na &%danie

191

Wst#pne wczytywanie kodu JavaScript

192

Podsumowanie

194

Skorowidz .................................................................................................................. 195

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

137

ROZDZIA+ 7.

Wzorce projektowe

Wzorce projektowe opisane w ksi%&ce tak zwanego gangu czworga oferuj% rozwi%zania ty-
powych problemów zwi%zanych z projektowaniem oprogramowania zorientowanego obiektowo.
S% dost#pne ju& od jakiego' czasu i sprawdzi$y si# w wielu ró&nych sytuacjach, warto wi#c
si# z nimi zapozna* i po'wi#ci* im nieco czasu.

Cho* same te wzorce projektowe nie s% uzale&nione od j#zyka programowania i implementa-
cji, by$y analizowane przez wiele lat g$ównie z perspektywy j#zyków o silnym sprawdzaniu
typów i statycznych (niezmiennych) klasach takich jak Java lub C++.

JavaScript jest j#zykiem o lu;nej kontroli typów i bazuje na prototypach (a nie klasach), wi#c nie-
które z tych wzorców okazuj% si# wyj%tkowo proste, a czasem wr#cz banalne w implementacji.

Zacznijmy od przyk$adu sytuacji, w której w j#zyku JavaScript rozwi%zanie wygl%da inaczej
ni& w przypadku j#zyków statycznych bazuj%cych na klasach, czyli od wzorca singletonu.

Singleton

Wzorzec singletonu ma w za$o&eniu zapewni* tylko jedn% instancj# danej klasy. Oznacza to,
&e próba utworzenia obiektu danej klasy po raz drugi powinna zwróci* dok$adnie ten sam
obiekt, który zosta$ zwrócony za pierwszym razem.

Jak zastosowa* ten wzorzec w j#zyku JavaScript? Nie mamy przecie& klas, a jedynie obiekty.
Gdy powstaje nowy obiekt, nie ma w zasadzie drugiego identycznego, wi#c jest on automa-
tycznie singletonem. Utworzenie prostego obiektu za pomoc% litera$u to doskona$y przyk$ad
utworzenia singletonu.

var obj = {
myprop: 'warto,-'
};

W JavaScripcie obiekty nie s% sobie równe, je'li nie s% dok$adnie tym samym obiektem,
wi#c nawet je'li utworzy si# dwa identyczne obiekty z takimi samymi warto'ciami, nie
b#d% równowa&ne.

var obj2 = {
myprop: 'warto,-'
};
obj === obj2; // false
obj == obj2; // false

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

138

Rozdzia$ 7. Wzorce projektowe

Mo&na wi#c stwierdzi*, &e za ka&dym razem, gdy powstaje nowy obiekt tworzony za pomoc%
litera$u, powstaje nowy singleton, i to bez u&ycia dodatkowej sk$adni.

Czasem gdy ludzie mówi% „singleton” w kontek'cie j#zyka JavaScript, maj% na my'li
wzorzec modu$u opisany w rozdziale 5.

U%ycie s$owa kluczowego new

JavaScript jest j#zykiem niestosuj%cym klas, wi#c dos$owna definicja singletonu nie ma tu za-
stosowania. Z drugiej strony j#zyk posiada s$owo kluczowe

new

, które tworzy obiekty na

podstawie funkcji konstruuj%cych. Czasem tworzenie ich w ten sposób jako singletonów mo-
&e by* ciekawym podej'ciem. Ogólny pomys$ jest nast#puj%cy: kilkukrotne wywo$anie funkcji
konstruuj%cej z u&yciem

new

powinno spowodowa* ka&dorazowo zwrócenie dok$adnie tego

samego obiektu.

Przedstawiony poni&ej opis nie jest u&yteczny w praktyce. Stanowi raczej teoretyczne
wyja'nienie powodów powstania wzorca w j#zykach statycznych o 'cis$ej kontroli
typów, w których to funkcje nie s% pe$noprawnymi obiektami.

Poni&szy przyk$ad ilustruje oczekiwane zachowanie (pod warunkiem &e nie wierzy si#
w 'wiaty równoleg$e i akceptuje si# tylko jeden).

var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

W tym przyk$adzie

uni

tworzone jest tylko przy pierwszym wywo$aniu konstruktora. Drugie

i kolejne wywo$ania zwracaj% ten sam obiekt. Dzi#ki temu

uni === uni2

(to dok$adnie ten

sam obiekt). Jak osi%gn%* taki efekt w j#zyku JavaScript?

Konstruktor

Universe

musi zapami#ta* instancj# obiektu (

this

), gdy zostanie utworzona po

raz pierwszy, a nast#pnie zwraca* j% przy kolejnych wywo$aniach. Istnieje kilka sposobów,
by to uzyska*.

"

Wykorzystanie zmiennej globalnej do zapami#tania instancji. Nie jest to zalecane podej-
'cie, bo zmienne globalne nale&y tworzy* tylko wtedy, gdy jest to naprawd# niezb#dne.
Co wi#cej, ka&dy mo&e nadpisa* tak% zmienn%, tak&e przez przypadek. Na tym zako+czmy
rozwa&ania dotycz%ce tej wersji.

"

Wykorzystanie w$a'ciwo'ci statycznej konstruktora. Funkcje w j#zyku JavaScript s%
obiektami, wi#c maj% w$a'ciwo'ci. Mo&na by utworzy* w$a'ciwo'*

Universe.instance

i to w niej przechowywa* obiekt. To eleganckie rozwi%zanie, ale ma jedn% wad#: w$a'ci-
wo'*

instance

by$aby dost#pna publicznie i inny kod móg$by j% zmieni*.

"

Zamkni#cie instancji w domkni#ciu. W ten sposób instancja staje si# elementem prywatnym
i nie mo&e zosta* zmieniona z zewn%trz. Cen% tego rozwi%zania jest dodatkowe domkni#cie.

Przyjrzyjmy si# przyk$adowym implementacjom drugiej i trzeciej opcji.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Singleton

139

Instancja we w$a"ciwo"ci statycznej

Poni&szy kod zapami#tuje pojedyncz% instancj# we w$a'ciwo'ci statycznej konstruktora

Universe

.

function Universe() {

// czy istnieje ju& instancja?
if (typeof Universe.instance === "object") {
return Universe.instance;
}

// standardowe dzia)ania
this.start_time = 0;
this.bang = "Wielki";

// zapami,tanie instancji
Universe.instance = this;

// niejawna instrukcja return:
// return this;
}

// test
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

To bardzo proste rozwi%zanie z jedn% wad%, któr% jest publiczne udost#pnienie

instance

.

Cho* prawdopodobie+stwo zmiany takiej w$a'ciwo'ci przez kod jest niewielkie (i na pewno
znacz%co mniejsze ni& w przypadku zmiennej globalnej), to jednak jest to mo&liwe.

Instancja w domkni#ciu

Innym sposobem uzyskania singletonu podobnego do rozwi%za+ klasowych jest u&ycie
domkni#cia w celu ochrony instancji. W implementacji mo&na wykorzysta* wzorzec prywat-
nej sk$adowej statycznej omówiony w rozdziale 5. Tajnym sk$adnikiem jest nadpisanie kon-
struktora.

function Universe() {

// zapami,tanie instancji
var instance = this;

// standardowe dzia)ania
this.start_time = 0;
this.bang = "Wielki";

// nadpisanie konstruktora
Universe = function () {
return instance;
};
}

// testy
var uni = new Universe();
var uni2 = new Universe();
uni === uni2; // true

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

140

Rozdzia$ 7. Wzorce projektowe

Za pierwszym razem zostaje wywo$any oryginalny konstruktor, który zwraca

this

w sposób

standardowy. Drugie i nast#pne wywo$ania wykonuj% ju& zmieniony konstruktor, który ma
dost#p do zmiennej prywatnej

instance

dzi#ki domkni#ciu i po prostu j% zwraca.

Przedstawiona implementacja jest w zasadzie przyk$adem wzorca samomodyfikuj%cej si#
funkcji z rozdzia$u 4. Wad% tego rozwi%zania opisan% we wspomnianym rozdziale jest to, &e
nadpisana funkcja (w tym przypadku konstruktor

Universe()

) utraci wszystkie w$a'ciwo'ci

dodane mi#dzy jej zdefiniowaniem i nadpisaniem. W tej konkretnej sytuacji nic z tego, co zo-
stanie dodane do prototypu

Universe()

po pierwszym obiekcie, nie b#dzie mog$o posiada*

referencji do instancji utworzonej przez oryginaln% implementacj#.

Dla uwidocznienia problemu wykonajmy krótki test. Najpierw kilka wierszy przygotowuj%cych:

// dodanie w)a.ciwo.ci do prototypu
Universe.prototype.nothing = true;

var uni = new Universe();

// ponowne dodanie w)a.ciwo.ci do prototypu
// po utworzeniu pierwszego obiektu
Universe.prototype.everything = true;

var uni2 = new Universe();

Oto w$a'ciwy test:

// tylko oryginalny prototyp jest powi1zany z obiektami
uni.nothing; // true
uni2.nothing; // true
uni.everything; // undefined
uni2.everything; // undefined

// wygl1da prawid)owo:
uni.constructor.name; // "Universe"

// ale to jest dziwne:
uni.constructor === Universe; // false

Powodem, dla którego w$a'ciwo'*

uni.constructor

nie jest ju& taka sama jak konstruktor

Universe()

, jest fakt, i&

uni.constructor

nadal wskazuje na oryginalny konstruktor zamiast

przedefiniowanego.

Je'li prototyp i referencja wskazuj%ca na konstruktor musz% dzia$a* prawid$owo, do wcze-
'niejszej implementacji trzeba wprowadzi* kilka poprawek.

function Universe() {

// zapami,tanie instancji
var instance;

// nadpisanie konstruktora
Universe = function Universe() {
return instance;
};

// przeniesienie w)a.ciwo.ci prototypu
Universe.prototype = this;

// instancja
instance = new Universe();

// zmiana referencji wskazuj1cej na konstruktor
instance.constructor = Universe;

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Fabryka

141

// w)a.ciwa funkcjonalno.4
instance.start_time = 0;
instance.bang = "Wielki";

return instance;
}

Teraz wszystkie testy powinny dzia$a* zgodnie z oczekiwaniami.

// aktualizacja prototypu i utworzenie instancji
Universe.prototype.nothing = true; // true
var uni = new Universe();
Universe.prototype.everything = true; // true
var uni2 = new Universe();

// to ta sama pojedyncza instancja
uni === uni2; // true

// wszystkie w)a.ciwo.ci prototypu dzia)aj1 prawid)owo
// niezale&nie od momentu ich zdefiniowania
uni.nothing && uni.everything && uni2.nothing && uni2.everything; // true
// standardowe w)a.ciwo.ci równie& dzia)aj1 prawid)owo
uni.bang; // "Wielki"
// referencja wskazuj1ca na konstruktor równie& jest prawid)owa
uni.constructor === Universe; // true

Alternatywne rozwi%zanie mog$oby polega* na otoczeniu konstruktora oraz instancji funkcj%
natychmiastow%. Pierwsze wywo$anie konstruktora tworzy obiekt i zapami#tuje go w pry-
watnej zmiennej

instance

. Drugie i kolejne wywo$ania jedynie zwracaj% zawarto'* zmiennej.

Wszystkie poprzednie testy b#d% dzia$a$y równie& dla implementacji przedstawionej poni&ej.

var Universe;

(function () {

var instance;

Universe = function Universe() {

if (instance) {
return instance;
}

instance = this;

// w)a.ciwa funkcjonalno.4
this.start_time = 0;
this.bang = "Wielki";

};

}());

Fabryka

Celem wzorca fabryki jest tworzenie obiektów. Najcz#'ciej fabryk% jest klasa lub metoda sta-
tyczna klasy, której celem jest:

"

wykonanie powtarzaj%cych si# operacji przy tworzeniu podobnych obiektów;

"

zapewnienie u&ytkownikom mo&liwo'ci tworzenia obiektów bez potrzeby znania kon-
kretnego typu (klasy) na etapie kompilacji.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

142

Rozdzia$ 7. Wzorce projektowe

Drugi punkt ma wi#ksze znaczenie w przypadku j#zyków ze statyczn% analiz% typów, w któ-
rych to utworzenie instancji klas nieznanych na etapie kompilacji nie jest zadaniem $atwym.
Na szcz#'cie w j#zyku JavaScript nie trzeba g$owi* si# nad tym zagadnieniem.

Obiekty tworzone przez metod# fabryczn% z regu$y dziedzicz% po tym samym przodku, ale
z drugiej strony s% wyspecjalizowanymi wersjami z pewnymi dodatkowymi rozwi%zaniami.
Czasem wspólny przodek to klasa zawieraj%ca metod# fabryczn%.

Przyjrzyjmy si# przyk$adowej implementacji, która ma:

"

wspólny konstruktor przodka

CarMaker

;

"

metod# statyczn%

CarMaker

o nazwie

factory()

, która tworzy obiekty samochodów;

"

wyspecjalizowane konstruktory

CarMaker.Compact

,

CarMaker.SUV

i

CarMaker.Convertible

,

które dziedzicz% po

CarMaker

i wszystkie s% statycznymi w$a'ciwo'ciami przodka, dzi#ki

czemu globalna przestrze+ nazw pozostaje czysta i $atwo je w razie potrzeby odnale;*.

Implementacja b#dzie mog$a by* wykorzystywana w nast#puj%cy sposób:

var corolla = CarMaker.factory('Compact');
var solstice = CarMaker.factory('Convertible');
var cherokee = CarMaker.factory('SUV');
corolla.drive(); // "Brum, mam 4 drzwi"
solstice.drive(); // "Brum, mam 2 drzwi"
cherokee.drive(); // "Brum, mam 17 drzwi"

Fragment

var corolla = CarMaker.factory('Compact');

to prawdopodobnie najbardziej rozpoznawalna cz#'* wzorca fabryki. Metoda przyjmuje typ
jako tekst i na jego podstawie tworzy i zwraca obiekty danego typu. Nie pojawiaj% si# kon-
struktory wykorzystuj%ce

new

lub litera$y obiektów — u&ytkownik stosuje funkcj#, która two-

rzy obiekty na podstawie typu wskazanego jako tekst.

Oto przyk$adowa implementacja wzorca fabryki, która odpowiada wcze'niejszemu przyk$a-
dowi jego u&ycia:

// konstruktor przodka
function CarMaker() {}

// metoda przodka
CarMaker.prototype.drive = function () {
return "Brum, mam " + this.doors + " drzwi";
};

// statyczna metoda fabryczna
CarMaker.factory = function (type) {
var constr = type,
newcar;

// b)1d, je.li konstruktor nie istnieje
if (typeof CarMaker[constr] !== "function") {
throw {
name: "Error",
message: constr + " nie istnieje"
};
}

// na tym etapie wiemy, &e konstruktor istnieje
// niech odziedziczy przodka, ale tylko raz

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Iterator

143

if (typeof CarMaker[constr].prototype.drive !== "function") {
CarMaker[constr].prototype = new CarMaker();
}
// utworzenie nowej instancji
newcar = new CarMaker[constr]();
// opcjonalne wywo)anie dodatkowych metod i zwrócenie obiektu...
return newcar;
};

// definicje konkretnych konstruktorów
CarMaker.Compact = function () {
this.doors = 4;
};
CarMaker.Convertible = function () {
this.doors = 2;
};
CarMaker.SUV = function () {
this.doors = 17;
};

W implementacji wzorca fabryki nie ma nic szczególnego. Wystarczy wyszuka* odpowiedni%
funkcj# konstruuj%c%, która utworzy obiekt wymaganego typu. W tym przypadku zastoso-
wano bardzo proste odwzorowanie nazw przekazywanych do fabryki na odpowiadaj%ce im
obiekty. Przyk$adem powtarzaj%cych si# zada+, które warto by$oby umie'ci* w fabryce, za-
miast powtarza* osobno dla ka&dego konstruktora, jest dziedziczenie.

Wbudowane fabryki obiektów

W zasadzie j#zyk JavaScript posiada wbudowan% fabryk#, któr% jest globalny konstruktor

Object()

. Zachowuje si# on jak fabryka, poniewa& zwraca ró&ne typy obiektów w zale&no'ci

od parametru wej'ciowego. Przekazanie liczby spowoduje utworzenie obiektu konstruktorem

Number()

. Podobnie dzieje si# dla tekstów i warto'ci logicznych. Wszystkie inne warto'ci lub

brak argumentu spowoduj% utworzenie zwyk$ego obiektu.
Oto kilka przyk$adów i testów tego sposobu dzia$ania. Co wi#cej,

Object

mo&na równie&

wywo$a* z takim samym efektem bez u&ycia

new

.

var o = new Object(),
n = new Object(1),
s = Object('1'),
b = Object(true);

// testy
o.constructor === Object; // true
n.constructor === Number; // true
s.constructor === String; // true
b.constructor === Boolean; // true

To, &e

Object()

jest równie& fabryk%, ma ma$e znaczenie praktyczne, ale warto o tym

wspomnie*, by mie* 'wiadomo'*, i& wzorzec fabryki pojawia si# niemal wsz#dzie.

Iterator

We wzorcu iteratora mamy do czynienia z pewnym obiektem zawieraj%cym zagregowane
dane. Dane te mog% by* przechowywane wewn#trznie w bardzo z$o&onej strukturze, ale se-
kwencyjny dost#p do nich zapewnia bardzo prosta funkcja. Kod korzystaj%cy z obiektu nie
musi zna* ca$ej z$o&ono'ci struktury danych — wystarczy, &e wie, jak korzysta* z poje-
dynczego elementu i pobra* nast#pny.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

144

Rozdzia$ 7. Wzorce projektowe

We wzorcu iteratora kluczow% rol# odgrywa metoda

next()

. Ka&de jej wywo$anie powinno

zwraca* nast#pny element w kolejce. To, jak u$o&ona jest kolejka i jak posortowane s% ele-
menty, zale&y od zastosowanej struktury danych.

Przy za$o&eniu, &e obiekt znajduje si# w zmiennej

agg

, dost#p do wszystkich elementów da-

nych uzyska si# dzi#ki wywo$ywaniu

next()

w p#tli:

var element;
while (element = agg.next()) {
// wykonanie dzia)a? na elemencie...
console.log(element);
}

We wzorcu iteratora bardzo cz#sto obiekt agreguj%cy zapewnia dodatkow% metod# pomocni-
cz%

hasNext()

, która informuje u&ytkownika, czy zosta$ ju& osi%gni#ty koniec danych. Inny

sposób uzyskania sekwencyjnego dost#pu do wszystkich elementów, tym razem z u&yciem

hasNext()

, móg$by wygl%da* nast#puj%co:

while (agg.hasNext()) {
// wykonanie dzia)a? na nast,pnym elemencie...
console.log(agg.next());
}

Po przedstawieniu sposobów u&ycia wzorca czas na implementacj# obiektu agreguj%cego.

Implementuj%c wzorzec iteratora, warto w zmiennej prywatnej przechowywa* dane oraz
wska;nik (indeks) do nast#pnego elementu. W naszym przyk$adzie za$ó&my, &e dane to ty-
powa tablica, a „specjalna” logika pobierania tak naprawd# zwraca jej nast#pny element.

var agg = (function () {

var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;

return {

next: function () {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},

hasNext: function () {
return index < length;
}

};
}());

Aby zapewni* $atwiejszy dost#p do danych i mo&liwo'* kilkukrotnej iteracji, obiekt mo&e
oferowa* dodatkowe metody:

"

rewind()

— ustawia wska;nik na pocz%tek kolejki;

"

current()

— zwraca aktualny element, bo nie mo&na tego uczyni* za pomoc%

next()

bez jednoczesnej zmiany wska;nika.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Dekorator

145

Implementacja tych dodatkowych metod nie sprawi &adnych trudno'ci.

var agg = (function () {

// [jak wy&ej...]

return {

// [jak wy&ej...]

rewind: function () {
index = 0;
},
current: function () {
return data[index];
}
};
}());

Oto dodatkowy test iteratora:

// p,tla wy.wietla warto.ci 1, 3 i 5
while (agg.hasNext()) {
console.log(agg.next());
}

// powrót na pocz1tek
agg.rewind();
console.log(agg.current()); // 1

W konsoli pojawi% si# nast#puj%ce warto'ci: 1, 3 i 5 (z p#tli), a na ko+cu ponownie 1 (po
przej'ciu na pocz%tek kolejki).

Dekorator

We wzorcu dekoratora dodatkow% funkcjonalno'* mo&na dodawa* do obiektu dynamicznie
w trakcie dzia$ania programu. W przypadku korzystania ze statycznych i niezmiennych klas
jest to faktycznie du&e wyzwanie. W j#zyku JavaScript obiekty mo&na modyfikowa*, wi#c
dodanie do nich nowej funkcjonalno'ci nie stanowi wielkiego problemu.

Dodatkow% cech% wzorca dekoratora jest $atwo'* dostosowania i konfiguracji jego oczekiwa-
nego zachowania. Zaczyna si# od prostego obiektu z podstawow% funkcjonalno'ci%. Nast#p-
nie wybiera si# kilka z zestawu dost#pnych dekoratorów, po czym rozszerza si# nimi pod-
stawowy obiekt. Czasem istotna jest kolejno'* tego rozszerzania.

Sposób u%ycia

Przyjrzyjmy si# sposobom u&ycia tego wzorca. Przypu'*my, &e opracowujemy aplikacj#, która
co' sprzedaje. Ka&da nowa sprzeda& to nowy obiekt

sale

. Obiekt zna cen# produktu i potrafi

j% zwróci* po wywo$aniu metody

sale.getPrice()

. W zale&no'ci od aktualnych warunków

mo&na zacz%* „dekorowa*” obiekt dodatkow% funkcjonalno'ci%. Wyobra;my sobie, &e jako
ameryka+ski sklep sprzedajemy produkt klientowi z kanadyjskiej prowincji Québec. W takiej
sytuacji klient musi zap$aci* podatek federalny i dodatkowo podatek lokalny. We wzorcu
dekoratora b#dziemy wi#c „dekorowali” obiekt dekoratorem podatku federalnego i dekora-
torem podatku lokalnego. Po wyliczeniu ceny ko+cowej mo&na równie& doda* dekorator do
jej formatowania. Scenariusz by$by nast#puj%cy:

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

146

Rozdzia$ 7. Wzorce projektowe

var sale = new Sale(100); // cena wynosi 100 dolarów
sale = sale.decorate('fedtax'); // dodaj podatek federalny
sale = sale.decorate('quebec'); // dodaj podatek lokalny
sale = sale.decorate('money'); // formatowanie ceny
sale.getPrice(); // "USD 112.88"

W innym scenariuszu kupuj%cy mo&e mieszka* w prowincji, która nie stosuje podatku lokal-
nego, i dodatkowo mo&emy chcie* poda* cen# w dolarach kanadyjskich.

var sale = new Sale(100); // cena wynosi 100 dolarów
sale = sale.decorate('fedtax'); // dodaj podatek federalny
sale = sale.decorate('cdn'); // sformatuj jako dolary kanadyjskie
sale.getPrice(); // "CAD 105.00"

Nietrudno zauwa&y*, &e jest to wygodny i elastyczny sposób dodawania lub modyfikowania
funkcjonalno'ci utworzonych ju& obiektów. Czas na implementacj# wzorca.

Implementacja

Jednym ze sposobów implementacji wzorca dekoratora jest utworzenie dekoratorów jako
obiektów zawieraj%cych metody do nadpisania. Ka&dy dekorator dziedziczy wówczas tak
naprawd# po obiekcie rozszerzonym przez poprzedni dekorator. Ka&da dekorowana metoda
wywo$uje swoj% poprzedniczk# za pomoc%

uber

(odziedziczony obiekt), pobiera warto'*

i przetwarza j%, dodaj%c co' nowego.

Efekt jest taki, &e wywo$anie metody

sale.getPrice()

z pierwszego z przedstawionych

przyk$adów powoduje tak naprawd# wywo$anie metody dekoratora

money

(patrz rysunek 7.1).

Poniewa& jednak ka&dy dekorator wywo$uje najpierw odpowiadaj%c% mu metod# ze swego
poprzednika,

getPrice()

z

money

wywo$uje

getPrice()

z

quebec

, a ta metod#

getPrice()

z

fedtax

i tak dalej. .a+cuch mo&e by* d$u&szy, ale ko+czy si# oryginaln% metod%

getPrice()

zaimplementowan% przez konstruktor

Sale()

.

Rysunek 7.1. Implementacja wzorca dekoratora

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Dekorator

147

Implementacja rozpoczyna si# od konstruktora i metody prototypu.

function Sale(price) {
this.price = price || 100;
}
Sale.prototype.getPrice = function () {
return this.price;
};

Wszystkie obiekty dekoratorów znajd% si# we w$a'ciwo'ci konstruktora:

Sale.decorators = {};

Przyjrzyjmy si# przyk$adowemu dekoratorowi. To obiekt implementuj%cy zmodyfikowan%
wersj# metody

getPrice()

. Metoda najpierw pobiera zwrócon% przez metod# przodka warto'*,

a nast#pnie j% modyfikuje.

Sale.decorators.fedtax = {
getPrice: function () {
var price = this.uber.getPrice();
price += price * 5 / 100;
return price;
}
};

W podobny sposób mo&na zaimplementowa* dowoln% liczb# innych dekoratorów. Mog% one
stanowi* rozszerzenie podstawowej funkcjonalno'ci

Sale()

, czyli dzia$a* jak dodatki. Co wi#cej,

nic nie stoi na przeszkodzie, by znajdowa$y si# w dodatkowych plikach i by$y implementowane
przez innych, niezale&nych programistów.

Sale.decorators.quebec = {
getPrice: function () {
var price = this.uber.getPrice();
price += price * 7.5 / 100;
return price;
}
};

Sale.decorators.money = {
getPrice: function () {
return "USD " + this.uber.getPrice().toFixed(2);
}
};

Sale.decorators.cdn = {
getPrice: function () {
return "CAD " + this.uber.getPrice().toFixed(2);
}
};

Na koniec przyjrzyjmy si# „magicznej” metodzie o nazwie

decorate()

, która $%czy ze sob%

wszystkie elementy. Sposób jej u&ycia jest nast#puj%cy:

sale = sale.decorate('fedtax');

Tekst

'fedtax'

odpowiada obiektowi zaimplementowanemu w

Sale.decorators.fedtax

.

Nowy obiekt

newobj

dziedziczy obiekt aktualny (orygina$ lub ju& udekorowan% wersj#), któ-

ry jest zawarty w

this

. Do zapewnienia dziedziczenia wykorzystajmy wzorzec konstruktora

tymczasowego z poprzedniego rozdzia$u. Dodatkowo ustawmy w$a'ciwo'*

uber

obiektu

newobj

, by potomek mia$ dost#p do przodka. Nast#pnie niech kod kopiuje wszystkie w$a-

'ciwo'ci z dekoratora do nowego obiektu i zwraca

newobj

jako wynik ca$ej operacji, co spo-

woduje, &e stanie si# on nowym obiektem

sale

.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

148

Rozdzia$ 7. Wzorce projektowe

Sale.prototype.decorate = function (decorator) {
var F = function () {},
overrides = this.constructor.decorators[decorator],
i, newobj;
F.prototype = this;
newobj = new F();
newobj.uber = F.prototype;
for (i in overrides) {
if (overrides.hasOwnProperty(i)) {
newobj[i] = overrides[i];
}
}
return newobj;
};

Implementacja wykorzystuj&ca list#

Przeanalizujmy inn% implementacj#, która korzysta z dynamicznej natury j#zyka JavaScript
i w ogóle nie stosuje dziedziczenia. Dodatkowo, zamiast wymusza* na ka&dej metodzie de-
koruj%cej, by wywo$ywa$a swoj% poprzedniczk#, przekazujemy tu wynik poprzedniej metody
jako parametr nast#pnej.

Taka implementacja znacz%co u$atwia wycofanie udekorowania, czyli usuni#cie jednego
z elementów z listy dekoratorów.

Sposób u&ycia nowej implementacji b#dzie prostszy, bo nie wymaga ona przypisywania
warto'ci zwróconej przez

decorate()

. W tym przypadku

decorate()

jedynie dodaje nowy

element do listy:

var sale = new Sale(100); // cena wynosi 100 dolarów
sale.decorate('fedtax'); // dodaj podatek federalny
sale.decorate('quebec'); // dodaj podatek lokalny
sale.decorate('money'); // formatowanie ceny
sale.getPrice(); // "USD 112.88"

Tym razem konstruktor

Sale()

zawiera list# dekoratorów jako w$asn% w$a'ciwo'*.

function Sale(price) {
this.price = (price > 0) || 100;
this.decorators_list = [];
}

Dost#pne dekoratory s% ponownie implementowane jako w$a'ciwo'ci

Sale.decorators

.

S% prostsze, bo nie musz% ju& wywo$ywa* poprzedniej wersji metody

getPrice()

, by uzy-

ska* warto'* po'redni%. Teraz trafia ona do systemu jako parametr.

Sale.decorators = {};

Sale.decorators.fedtax = {
getPrice: function (price) {
return price + price * 5 / 100;
}
};

Sale.decorators.quebec = {
getPrice: function (price) {
return price + price * 7.5 / 100;
}
};

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Strategia

149

Sale.decorators.money = {
getPrice: function (price) {
return "USD " + price.toFixed(2);
}
};

Interesuj%ce konstrukcje pojawiaj% si# w metodach

decorate()

i

getPrice()

oryginalnego

obiektu. W poprzedniej implementacji metoda

decorate()

by$a w miar# z$o&ona, a

getPrice()

niezwykle prosta. W nowej jest dok$adnie odwrotnie —

decorate()

po prostu dodaje nowy

element do listy, a

getPrice()

wykonuje ca$% istotn% prac#. Prac% t% jest przej'cie przez list#

wszystkich dodanych dekoratorów i wywo$anie dla ka&dego z nich metody

getPrice()

z po-

przedni% warto'ci% podan% jako argument metody.

Sale.prototype.decorate = function (decorator) {
this.decorators_list.push(decorator);
};

Sale.prototype.getPrice = function () {
var price = this.price,
i,
max = this.decorators_list.length,
name;
for (i = 0; i < max; i += 1) {
name = this.decorators_list[i];
price = Sale.decorators[name].getPrice(price);
}
return price;
};

Druga implementacja jest prostsza i nie korzysta z dziedziczenia. Prostsze s% równie& metody
dekoruj%ce. Ca$% rzeczywist% prac# wykonuje metoda, która „zgadza” si# na dekoracj#. W tej
prostej implementacji dekoracj# dopuszcza jedynie metoda

getPrice()

. Je'li dekoracja mia-

$aby dotyczy* wi#kszej liczby metod, ka&da z nich musia$aby przej'* przez list# dekoratorów
i wywo$a* odpowiednie metody. Oczywi'cie taki kod stosunkowo $atwo jest umie'ci*
w osobnej metodzie pomocniczej i uogólni*. Umo&liwia$by on dodanie dekorowalno'ci do
dowolnej metody. Co wi#cej, w takiej implementacji w$a'ciwo'*

decorators_list

by$aby

obiektem z w$a'ciwo'ciami o nazwach metod i z tablicami dekorowanych obiektów jako
warto'ciami.

Strategia

Wzorzec strategii umo&liwia wybór odpowiedniego algorytmu na etapie dzia$ania aplikacji.
U&ytkownicy kodu mog% stosowa* ten sam interfejs zewn#trzny, ale wybiera* spo'ród kilku
dost#pnych algorytmów, by lepiej dopasowa* implementacj# do aktualnego kontekstu.

Przyk$adem wzorca strategii mo&e by* rozwi%zywanie problemu walidacji formularzy. Mo&na
utworzy* jeden obiekt sprawdzania z metod%

validate()

. Metoda zostanie wywo$ana nie-

zale&nie od rodzaju formularza i zawsze zwróci ten sam wynik — list# danych, które nie s%
poprawne, wraz z komunikatami o b$#dach.

W zale&no'ci od sprawdzanych danych i typu formularza u&ytkownik kodu mo&e wybra*
ró&ne rodzaje sprawdze+. Walidator wybiera najlepsz% strategi) wykonania zadania i dele-
guje konkretne czynno'ci sprawdze+ do odpowiednich algorytmów.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

150

Rozdzia$ 7. Wzorce projektowe

Przyk$ad walidacji danych

Przypu'*my, &e mamy do czynienia z nast#puj%cym zestawem danych pochodz%cym naj-
prawdopodobniej z formularza i &e chcemy go sprawdzi* pod k%tem poprawno'ci:

var data = {
first_name: "Super",
last_name: "Man",
age: "unknown",
username: "o_O"
};

Aby walidator zna$ najlepsz% strategi# do zastosowania w tym konkretnym przyk$adzie, trzeba
najpierw go skonfigurowa*, okre'laj%c zestaw regu$ i warto'ci uznawanych za prawid$owe.

Przypu'*my, &e nie wymagamy podania nazwiska i zaakceptujemy dowoln% warto'* imienia,
ale wymagamy podania wieku jako liczby i nazwy u&ytkownika, która sk$ada si# tylko z liczb
i liter bez znaków specjalnych. Konfiguracja mog$aby wygl%da* nast#puj%co:

validator.config = {
first_name: 'isNonEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};

Po skonfigurowaniu obiektu

validator

jest on gotowy do przyj#cia danych. Wywo$ujemy

jego metod#

validate()

i wy'wietlamy b$#dy walidacji w konsoli.

validator.validate(data);
if (validator.hasErrors()) {
console.log(validator.messages.join("\n"));
}

Efektem wykonania kodu móg$by by* nast#puj%cy komunikat:

Niepoprawna warto,- *age*; warto,- musi by- liczb^, na przyk_ad 1, 3.14 lub 2010
Niepoprawna warto,- *username*; warto,- musi zawiera- jedynie litery i cyfry bez
`adnych znaków specjalnych

Przyjrzyjmy si# implementacji walidatora. Poszczególne algorytmy s% obiektami o z góry
ustalonym interfejsie — zawieraj% metod#

validate()

i jednowierszow% informacj# wyko-

rzystywan% jako komunikat o b$#dzie.

// sprawdzenie, czy podano jak1. warto.4
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "warto,- nie mo`e by- pusta"
};

// sprawdzenie, czy warto.4 jest liczb1
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "warto,- musi by- liczb^, na przyk_ad 1, 3.14 lub 2010"
};

// sprawdzenie, czy warto.4 zawiera jedynie litery i cyfry
validator.types.isAlphaNum = {
validate: function (value) {

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Strategia

151

return !/[^a-z0-9]/i.test(value);
},
instructions: "warto,- musi zawiera- jedynie litery i cyfry bez `adnych znaków
specjalnych"
};

Najwy&szy czas na obiekt

validator

:

var validator = {

// wszystkie dost,pne sprawdzenia
types: {},

// komunikaty o b),dach
// z aktualnej sesji walidacyjnej
messages: [],

// aktualna konfiguracja walidacji
// nazwa => rodzaj testu
config: {},

// metoda interfejsu
// data to pary klucz-warto.4
validate: function (data) {

var i, msg, type, checker, result_ok;

// usuni,cie wszystkich komunikatów
this.messages = [];

for (i in data) {

if (data.hasOwnProperty(i)) {

type = this.config[i];
checker = this.types[type];

if (!type) {
continue; // nie trzeba sprawdza4
}
if (!checker) { // ojej
throw {
name: "ValidationError",
message: "Brak obs_ugi dla klucza " + type
};
}

result_ok = checker.validate(data[i]);
if (!result_ok) {
msg = "Niepoprawna warto,- *" + i + "*; " + checker.instructions;
this.messages.push(msg);
}
}
}
return this.hasErrors();
},

// metoda pomocnicza
hasErrors: function () {
return this.messages.length !== 0;
}
};

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

152

Rozdzia$ 7. Wzorce projektowe

Obiekt

validator

jest uniwersalny i b#dzie dzia$a$ prawid$owo dla ró&nych rodzajów spraw-

dze+. Jednym z usprawnie+ mog$oby by* dodanie kilku nowych testów. Po wykonaniu kilku
ró&nych formularzy z walidacj% Twoja lista dost#pnych sprawdze+ z pewno'ci% si# wyd$u&y.
Ka&dy kolejny formularz b#dzie wymaga$ jedynie skonfigurowania walidatora i uruchomie-
nia metody

validate()

.

Fasada

Wzorzec fasady jest bardzo prosty i ma za zadanie zapewni* alternatywny interfejs obiektu.
Dobr% praktyk% jest stosowanie krótkich metod, które nie wykonuj% zbyt wielu zada+. Stosuj%c
to podej'cie, uzyskuje si# znacznie wi#cej metod ni& w przypadku tworzenia supermetod z wie-
loma parametrami. W wi#kszo'ci sytuacji dwie lub wi#cej metod wykonuje si# jednocze'nie.
Warto wtedy utworzy* jeszcze jedn% metod#, która stanowi otoczk# dla takich po$%cze+.

W trakcie obs$ugi zdarze+ przegl%darki bardzo cz#sto korzysta si# z nast#puj%cych metod:

"

stopPropagation()

— zapobiega wykonywaniu obs$ugi zdarzenia w w#z$ach nadrz#dnych;

"

preventDefault()

— zapobiega wykonaniu przez przegl%dark# domy'lnej akcji dla zda-

rzenia (na przyk$ad klikni#cia $%cza lub wys$ania formularza).

To dwie osobne metody wykonuj%ce odmienne zadania, wi#c nie stanowi% jednej ca$o'ci, ale z dru-
giej strony w zdecydowanej wi#kszo'ci sytuacji s% one wykonywane jednocze'nie. Zamiast wi#c
powiela* wywo$ania obu metod w ca$ej aplikacji, mo&na utworzy* fasad#, która je obie wykona.

var myevent = {
// ...
stop: function (e) {
e.preventDefault();
e.stopPropagation();
}
// ...
};

Wzorzec fasady przydaje si# równie& w sytuacjach, w których za fasad% warto ukry* ró&nice
pomi#dzy przegl%darkami internetowymi. Nic nie stoi na przeszkodzie, by rozbudowa* po-
przedni przyk$ad o inny sposób obs$ugi anulowania zdarze+ przez przegl%dark# IE.

var myevent = {
// ...
stop: function (e) {
// inne
if (typeof e.preventDefault === "function") {
e.preventDefault();
}
if (typeof e.stopPropagation === "function") {
e.stopPropagation();
}
// IE
if (typeof e.returnValue === "boolean") {
e.returnValue = false;
}
if (typeof e.cancelBubble === "boolean") {
e.cancelBubble = true;
}
}
// ...
};

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Po"rednik

153

Wzorzec fasady bywa pomocny w przypadku zmiany interfejsów zwi%zanej na przyk$ad
z refaktoryzacj%. Gdy chce si# zamieni* obiekt na inn% implementacj#, najcz#'ciej mo&e to
zaj%* sporo czasu (je'li jest on naprawd# rozbudowany). Za$ó&my te&, &e ju& powstaje kod
dla nowego interfejsu. W takiej sytuacji mo&na utworzy* przed starym obiektem fasad#, która
imituje nowy interfejs. W ten sposób po dokonaniu rzeczywistej zamiany i pozbyciu si# starego
obiektu ilo'* zmian w najnowszym kodzie zostanie ograniczona do minimum.

Po"rednik

We wzorcu projektowym po'rednika jeden obiekt stanowi interfejs dla innego obiektu. Ró&ni
si# to od wzorca fasady, w którym po prostu istniej% pewne metody dodatkowe $%cz%ce
w sobie wywo$ania kilku innych metod. Po'rednik znajduje si# mi#dzy u&ytkownikiem
a obiektem i broni dost#pu do niego.

Cho* wzorzec wygl%da jak dodatkowy narzut, w rzeczywisto'ci cz#sto s$u&y do poprawy
wydajno'ci. Po'rednik staje si# stra&nikiem rzeczywistego obiektu i stara si#, by ten wykona$
jak najmniej pracy.

Jednym z przyk$adów zastosowania po'rednika jest tak zwana leniwa inicjalizacja. Stosuje
si# j% w sytuacjach, w których inicjalizacja rzeczywistego obiektu jest kosztowna, a istnieje
spora szansa, &e klient po jego zainicjalizowaniu tak naprawd# nigdy go nie u&yje. Po'rednik
mo&e wtedy stanowi* interfejs dla rzeczywistego obiektu. Otrzymuje polecenie inicjalizacji,
ale nie przekazuje go dalej a& do momentu, gdy rzeczywisty obiekt naprawd# zostanie u&yty.

Rysunek 7.2 ilustruje sytuacj#, w której klient wysy$a polecenie inicjalizuj%ce, a po'rednik
odpowiada, &e wszystko jest w porz%dku, cho* tak naprawd# nie przekazuje polecenia dalej.
Czeka z inicjalizacj% w$a'ciwego obiektu do czasu, gdy klient rzeczywi'cie b#dzie wykony-
wa$ na nim prac# — wówczas przekazuje obydwa komunikaty.

Rysunek 7.2. Komunikacja mi.dzy klientem i rzeczywistym obiektem z wykorzystaniem po0rednika

Przyk$ad

Wzorzec po'rednika bywa przydatny, gdy rzeczywisty obiekt docelowy wykonuje kosztow-
ne zadanie. W aplikacjach internetowych jedn% z kosztownych sytuacji jest &%danie sieciowe,
wi#c w miar# mo&liwo'ci warto zebra* kilka operacji i wykona* je jednym &%daniem. Prze-
'led;my praktyczne zastosowanie wzorca w$a'nie w takiej sytuacji.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

154

Rozdzia$ 7. Wzorce projektowe

Aplikacja wideo

Za$ó&my istnienie prostej aplikacji odtwarzaj%cej materia$ wideo wybranego artysty (patrz
rysunek 7.3). W zasadzie mo&esz nawet przetestowa* kod, wpisuj%c w przegl%darce interne-
towej adres http://www.jspatterns.com/book/7/proxy.html.

Rysunek 7.3. Aplikacja wideo w akcji

Strona zawiera list# tytu$ów materia$ów wideo. Gdy u&ytkownik kliknie tytu$, obszar poni&ej
rozszerzy si#, by przedstawi* dodatkowe informacje i umo&liwi* odtworzenie filmu. Szcze-
gó$y dotycz%ce materia$ów oraz adres URL tre'ci wideo nie stanowi% cz#'ci strony — s% po-
bierane poprzez osobne wywo$ania serwera. Serwer przyjmuje jako parametr kilka identyfi-
katorów materia$ów wideo, wi#c aplikacj# mo&na przyspieszy*, wykonuj%c mniej &%da+
HTTP i pobieraj%c za ka&dym razem dane kilku filmów.

Aplikacja umo&liwia jednoczesne rozwini#cie szczegó$ów kilku (a nawet wszystkich) mate-
ria$ów, co stanowi doskona$% okazj# do po$%czenia kilku &%da+ w jedno.

Bez u%ycia po"rednika

G$ównymi elementami aplikacji s% dwa obiekty:

"

videos

— jest odpowiedzialny za rozwijanie i zwijanie obszarów informacyjnych (metoda

videos.getInfo()

) oraz za odtwarzanie materia$ów wideo (metoda

videos.getPlayer()

).

"

http

— jest odpowiedzialny za komunikacj# z serwerem za pomoc% metody

http.make

Request()

.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Po"rednik

155

Bez stosowania po'rednika

videos.getInfo()

wywo$a

http.makeRequest()

dla ka&dego

materia$u wideo. Po dodaniu po'rednika pojawi si# nowy obiekt o nazwie

proxy

znajduj%cy

si# mi#dzy

videos

oraz

http

i deleguj%cy wszystkie wywo$ania

makeRequest()

, a tak&e $%-

cz%cy je ze sob%.

Najpierw pojawi si# kod, w którym nie zastosowano wzorca po'rednika. Druga wersja, sto-
suj%ca obiekt po'rednika, poprawi ogóln% p$ynno'* dzia$ania aplikacji.

Kod HTML

Kod HTML to po prostu zbiór $%czy.

<p><span id="toggle-all">Prze_^cz zaznaczone</span></p>
<ol id="vids">
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2158073">Gravedigger</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--4472739">Save Me</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--45286339">Crush</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2144530">Don't Drink The Water
</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--217241800">Funny the Way It Is
</a></li>
<li><input type="checkbox" checked><a
href="http://new.music.yahoo.com/videos/--2144532">What Would You Say</a></li>
</ol>

Obs$uga zdarze?

Zanim pojawi si# w$a'ciwa obs$uga zdarze+, warto doda* funkcj# pomocnicz%

$

do pobiera-

nia elementów DOM na podstawie ich identyfikatorów.

var $ = function (id) {
return document.getElementById(id);
};

Stosuj%c delegacj# zdarze+ (wi#cej na ten temat w rozdziale 8.), mo&na obs$u&y* wszystkie
klikni#cia dotycz%ce listy uporz%dkowanej

id="vids"

za pomoc% jednej funkcji.

$('vids').onclick = function (e) {
var src, id;

e = e || window.event;
src = e.target || e.srcElement;

if (src.nodeName !== "A") {
return;
}

if (typeof e.preventDefault === "function") {
e.preventDefault();
}
e.returnValue = false;

id = src.href.split('--')[1];

if (src.className === "play") {

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

156

Rozdzia$ 7. Wzorce projektowe

src.parentNode.innerHTML = videos.getPlayer(id);
return;
}

src.parentNode.id = "v" + id;
videos.getInfo(id);
};

Obs$uga klikni#cia zainteresowana jest tak naprawd# dwoma sytuacjami: pierwsz% dotycz%c%
rozwini#cia lub zamkni#cia cz#'ci informacyjnej (wywo$anie

getInfo()

) i drug% zwi%zan%

z odtworzeniem materia$u wideo (gdy klikni#cie dotyczy$o obiektu z klas%

play

), co oznacza,

&e rozwini#cie ju& nast%pi$o i mo&na bezpiecznie wywo$a* metod#

getPlayer()

. Identyfika-

tory materia$ów wideo wydobywa si# z atrybutów

href

$%czy.

Druga z funkcji obs$uguj%cych klikni#cia dotyczy sytuacji, w której u&ytkownik chce prze$%-
czy* wszystkie cz#'ci informacyjne. W zasadzie sprowadza si# ona do wywo$ywania w p#tli
metody

getInfo()

.

$('toggle-all').onclick = function (e) {

var hrefs,
i,
max,
id;

hrefs = $('vids').getElementsByTagName('a');
for (i = 0, max = hrefs.length; i < max; i += 1) {
// pomi? )1cza odtwarzania
if (hrefs[i].className === "play") {
continue;
}
// pomi? niezaznaczone
if (!hrefs[i].parentNode.firstChild.checked) {
continue;
}

id = hrefs[i].href.split('--')[1];
hrefs[i].parentNode.id = "v" + id;
videos.getInfo(id);
}
};

Obiekt videos

Obiekt

videos

zawiera trzy metody:

"

getPlayer()

— zwraca kod HTML niezb#dny do odtworzenia materia$u wideo (nie-

istotny w rozwa&aniach na temat obiektu po'rednika).

"

updateList()

— wywo$anie zwrotne otrzymuj%ce wszystkie dane z serwera i generuj%ce

kod HTML do wykorzystania przy rozwijaniu szczegó$ów filmów (w tej metodzie rów-
nie& nie dzieje si# nic interesuj%cego).

"

getInfo()

— metoda prze$%czaj%ca widoczno'* cz#'ci informacyjnych i wykonu-

j%ca metody obiektu

http

przez przekazanie

updateList()

jako funkcji wywo$ania

zwrotnego.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Po"rednik

157

Oto istotny fragment obiektu

videos

:

var videos = {

getPlayer: function (id) {...},
updateList: function (data) {...},

getInfo: function (id) {

var info = $('info' + id);

if (!info) {
http.makeRequest([id], "videos.updateList");
return;
}

if (info.style.display === "none") {
info.style.display = '';
} else {
info.style.display = 'none';
}
}
};

Obiekt http

Obiekt

http

ma tylko jedn% metod#, która wykonuje &%danie JSONP do us$ugi YQL firmy Yahoo.

var http = {
makeRequest: function (ids, callback) {
var url = 'http://query.yahooapis.com/v1/public/yql?q=',
sql = 'select * from music.video.id where ids IN ("%ID%")',
format = "format=json",
handler = "callback=" + callback,
script = document.createElement('script');

sql = sql.replace('%ID%', ids.join('","'));
sql = encodeURIComponent(sql);

url += sql + '&' + format + '&' + handler;
script.src = url;

document.body.appendChild(script);
}
};

YQL (Yahoo! Query Language) to uogólniona us$uga internetowa, która oferuje mo&-
liwo'* korzystania ze sk$adni przypominaj%cej SQL do pobierania danych z innych
us$ug. W ten sposób nie trzeba poznawa* szczegó$ów ich API.

Gdy jednocze'nie prze$%czone zostan% wszystkie materia$y wideo, do serwera trafi sze'*
osobnych &%da+; ka&de b#dzie podobne do nast#puj%cego &%dania YQL:

select * from music.video.id where ids IN ("2158073")

Obiekt proxy

Zaprezentowany wcze'niej kod dzia$a prawid$owo, ale mo&na go zoptymalizowa*. Na scen#
wkracza obiekt

proxy

, który przejmuje komunikacj# mi#dzy

http

i

videos

. Obiekt stara si#

po$%czy* ze sob% kilka &%da+, czekaj%c na ich zebranie 50 ms. Obiekt

videos

nie wywo$uje

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

158

Rozdzia$ 7. Wzorce projektowe

us$ugi HTTP bezpo'rednio, ale przez po'rednika. Ten czeka krótk% chwil# z wys$aniem &%-
dania. Je'li wywo$ania z

videos

b#d% przychodzi$y w odst#pach krótszych ni& 50 ms, zostan%

po$%czone w jedno &%danie. Takie opó;nienie nie jest szczególnie widoczne, ale pomaga zna-
cz%co przyspieszy* dzia$anie aplikacji w przypadku jednoczesnego ods$aniania wi#cej ni&
jednego materia$u wideo. Co wi#cej, jest równie& przyjazne dla serwera, który nie musi ob-
s$ugiwa* sporej liczby &%da+.

Zapytanie YQL dla dwóch materia$ów wideo mo&e mie* posta*:

select * from music.video.id where ids IN ("2158073", "123456")

W istniej%cym kodzie zachodzi tylko jedna zmiana: metoda

videos.getInfo()

wywo$uje

metod#

proxy.makeRequest()

zamiast metody

http.makeRequest()

.

proxy.makeRequest(id, videos.updateList, videos);

Obiekt po'rednika korzysta z kolejki, w której gromadzi identyfikatory materia$ów wideo przeka-
zane w ostatnich 50 ms. Nast#pnie przekazuje wszystkie identyfikatory, wywo$uj%c metod# obiektu

http

i przekazuj%c w$asn% funkcj# wywo$ania zwrotnego, poniewa&

videos.updateList()

potrafi

przetworzy* tylko pojedynczy rekord danych.

Oto kod obiektu po'rednicz%cego

proxy

:

var proxy = {
ids: [],
delay: 50,
timeout: null,
callback: null,
context: null,
makeRequest: function (id, callback, context) {

// dodanie do kolejki
this.ids.push(id);

this.callback = callback;
this.context = context;

// ustawienie funkcji czasowej
if (!this.timeout) {
this.timeout = setTimeout(function () {
proxy.flush();
}, this.delay);
}
},
flush: function () {

http.makeRequest(this.ids, "proxy.handler");

// wyczyszczenie kolejki i funkcji czasowej
this.timeout = null;
this.ids = [];
},
handler: function (data) {
var i, max;

// pojedynczy materia) wideo
if (parseInt(data.query.count, 10) === 1) {
proxy.callback.call(proxy.context, data.query.results.Video);
return;
}

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Po"rednik

159

// kilka materia)ów wideo
for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {
proxy.callback.call(proxy.context, data.query.results.Video[i]);
}
}
};

Wprowadzenie po'rednika umo&liwi$o po$%czenie kilku &%da+ pobrania danych w jedno po-
przez zmian# tylko jednego wiersza oryginalnego kodu.

Rysunki 7.4 i 7.5 przedstawiaj% scenariusze z trzema osobnymi &%daniami (bez po'rednika)
i z jednym po$%czonym &%daniem (po u&yciu po'rednika).

Rysunek 7.4. Trzy osobne =>dania do serwera

Rysunek 7.5. Wykorzystanie po0rednika do zmniejszenia liczby =>daA wysyBanych do serwera

Po"rednik jako pami#B podr#czna

W prezentowanym przyk$adzie obiekt

videos

&%daj%cy danych jest na tyle inteligentny, &e

nie &%da tych samych informacji dwukrotnie. Nie zawsze jednak musi tak by*. Po'rednik
mo&e pój'* o krok dalej i chroni* rzeczywisty obiekt

http

przed powielaniem tych samych

&%da+, zapami#tuj%c je w nowej w$a'ciwo'ci

cache

(patrz rysunek 7.6). Gdyby obiekt

videos

ponownie poprosi$ o informacje o tym samym materiale (ten sam identyfikator), po'rednik
wydoby$by dane z pami#ci podr#cznej i unikn%$ komunikacji z serwerem.

Rysunek 7.6. Pami.E podr.czna w obiekcie po0rednika

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

160

Rozdzia$ 7. Wzorce projektowe

Mediator

Aplikacje — du&e czy ma$e — sk$adaj% si# z wielu obiektów. Obiekty musz% si# ze sob% ko-
munikowa* w sposób, który nie uczyni przysz$ej konserwacji kodu prawdziw% drog% przez
m#k# i umo&liwi bezpieczn% zmian# jednego fragmentu bez potrzeby przepisywania wszyst-
kich innych. Gdy aplikacja si# rozrasta, pojawiaj% si# coraz to nowe obiekty. W trakcie refak-
toryzacji obiekty usuwa si# lub przerabia. Gdy wiedz% o sobie za du&o i komunikuj% si#
bezpo'rednio (wywo$uj% si# wzajemnie i modyfikuj% w$a'ciwo'ci), powstaje mi#dzy nimi
niepo&%dany 1cis2y zwi3zek. Je'li obiekty s% ze sob% powi%zane zbyt mocno, nie$atwo zmie-
ni* jeden z nich bez modyfikacji pozosta$ych. Wtedy nawet najprostsza zmiana w aplikacji
nie jest d$u&ej trywialna i bardzo trudno oszacowa*, ile tak naprawd# czasu trzeba b#dzie
na ni% po'wi#ci*.

Wzorzec mediatora ma za zadanie promowa* lu4ne powi3zania obiektów i wspomóc przy-
sz$% konserwacj# kodu (patrz rysunek 7.7). W tym wzorcu niezale&ne obiekty (koledzy) nie
komunikuj% si# ze sob% bezpo'rednio, ale korzystaj% z obiektu mediatora. Gdy jeden z kole-
gów zmieni stan, informuje o tym mediator, a ten przekazuje t# informacj# wszystkim innym
zainteresowanym kolegom.

Rysunek 7.7. Uczestnicy wzorca mediatora

Przyk$ad mediatora

Prze'led;my przyk$ad u&ycia wzorca mediatora. Aplikacja b#dzie gr%, w której dwóch gra-
czy przez pó$ minuty stara si# jak najcz#'ciej klika* w przycisk. Pierwszy gracz naciska kla-
wisz nr 1, a drugi klawisz 0 (spory odst#p mi#dzy klawiszami zapewnia, &e nie pobij% si#
o klawiatur#). Tablica wyników pokazuje aktualny stan rywalizacji.

Obiektami uczestnicz%cymi w wymianie informacji s%:

"

pierwszy gracz,

"

drugi gracz,

"

tablica,

"

mediator.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Mediator

161

Mediator wie o wszystkich obiektach. Komunikuje si# z urz%dzeniem wej'ciowym (klawiatur%),
obs$uguje naci'ni#cia klawiszy, okre'la, który gracz jest aktywny, i informuje o zmianach
wyników (patrz rysunek 7.8). Gracz jedynie gra (czyli aktualizuje swój w$asny wynik) i in-
formuje mediator o tym zdarzeniu. Mediator informuje tablic# o zmianie wyniku, a ta aktu-
alizuje wy'wietlan% warto'*.

Rysunek 7.8. Uczestnicy w grze na szybko0E naciskania klawiszy

Poza mediatorem &aden inny obiekt nie wie nic o pozosta$ych. Dzi#ki temu bardzo $atwo
zaktualizowa* gr#, na przyk$ad doda* nowego gracza lub zmieni* tablic# wyników na wersj#
wy'wietlaj%c% pozosta$y czas.

Pe$na wersja gry wraz z kodem ;ród$owym jest dost#pna pod adresem http://www.jspatterns.com/
book/7/mediator.html

.

Obiekty graczy s% tworzone przy u&yciu konstruktora

Player()

i zawieraj% w$asne w$a'ci-

wo'ci

points

i

name

. Metoda

play()

z prototypu zwi#ksza liczb# punktów o jeden i infor-

muje o tym fakcie mediator.

function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function () {
this.points += 1;
mediator.played();
};

Obiekt

scoreboard

zawiera metod#

update()

wywo$ywan% przez mediator po zdobyciu

punktu przez jednego z graczy. Tablica nie wie nic o graczach i nie przechowuje wyniku —
po prostu wy'wietla informacje przekazane przez mediator.

var scoreboard = {

// aktualizowany element HTML
element: document.getElementById('results'),

// aktualizacja wy.wietlacza
update: function (score) {

var i, msg = '';
for (i in score) {
if (score.hasOwnProperty(i)) {

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

162

Rozdzia$ 7. Wzorce projektowe

msg += '<p><strong>' + i + '<\/strong>: ';
msg += score[i];
msg += '<\/p>';
}
}
this.element.innerHTML = msg;
}
};

Czas na obiekt mediatora. Odpowiada on za inicjalizacj# gry oraz utworzenie obiektów graczy
w metodzie

setup()

i 'ledzenie ich poczyna+ dzi#ki umieszczeniu ich we w$a'ciwo'ci

players

.

Metoda

played()

zostaje wywo$ana przez ka&dego z graczy po wykonaniu akcji. Aktualizuje ona

wynik (

score

) i przesy$a go do tablicy (

scoreboard

). Ostatnia metoda,

keypress()

, obs$uguje

zdarzenia klawiatury, okre'la, który gracz jest aktywny, i powiadamia go o wykonanej akcji.

var mediator = {

// wszyscy gracze
players: {},

// inicjalizacja
setup: function () {
var players = this.players;
players.home = new Player('Gospodarze');
players.guest = new Player('Go,cie');
},

// kto. zagra), uaktualnij wynik
played: function () {
var players = this.players,
score = {
"Gospodarze": players.home.points,
"Go,cie": players.guest.points
};

scoreboard.update(score);
},

// obs)uga interakcji z u&ytkownikiem
keypress: function (e) {
e = e || window.event; // IE
if (e.which === 49) { // klawisz "1"
mediator.players.home.play();
return;
}
if (e.which === 48) { // klawisz "0"
mediator.players.guest.play();
return;
}
}
};

Ostatni element to uruchomienie i zako+czenie gry.

// start!
mediator.setup();
window.onkeypress = mediator.keypress;

// gra ko?czy si, po 30 sekundach
setTimeout(function () {
window.onkeypress = null;
alert('Koniec gry!');
}, 30000);

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Obserwator

163

Obserwator

Wzorzec obserwatora jest niezwykle cz#sto wykorzystywany w programowaniu po stronie
klienta w j#zyku JavaScript. Wszystkie zdarzenia przegl%darki (poruszenie mysz%, naci'ni#cie
klawisza itp.) to przyk$ady jego u&ycia. Inn% cz#sto pojawiaj%c% si# nazw% tego wzorca s%
zdarzenia w2asne

, czyli zdarzenia tworzone przez programist#, a nie przegl%dark#. Jeszcze

inna nazwa to wzorzec subskrybenta-dostawcy.

G$ównym celem u&ywania wzorca jest promowanie lu;nego powi%zania elementów. Zamiast
sytuacji, w której jeden obiekt wywo$uje metod# drugiego, mamy sytuacj#, w której drugi
z obiektów zg$asza ch#* otrzymywania powiadomie+ o zmianie w pierwszym obiekcie. Sub-
skrybenta nazywa si# cz#sto obserwatorem, a obiekt obserwowany obiektem publikuj%cym
lub ;ród$em. Obiekt publikuj%cy wywo$uje subskrybentów po zaj'ciu istotnego zdarzenia
i bardzo cz#sto przekazuje informacj# o nim w postaci obiektu zdarzenia.

Pierwszy przyk$ad — subskrypcja magazynu

Aby dowiedzie* si#, jak zaimplementowa* wzorzec, pos$u&my si# konkretnym przyk$adem.
Przypu'*my, &e mamy wydawc#

paper

, który publikuje gazet# codzienn% i miesi#cznik. Sub-

skrybent

joe

zostanie powiadomiony o wydaniu nowego periodyku.

Obiekt

paper

musi zawiera* w$a'ciwo'*

subscribers

, która jest tablic% przechowuj%c%

wszystkich subskrybentów. Zg$oszenie si# do subskrypcji polega jedynie na dodaniu nowego
wpisu do tablicy. Gdy zajdzie istotne zdarzenie, obiekt

paper

przejdzie w p#tli przez wszyst-

kich subskrybentów, by ich o nim powiadomi*. Notyfikacja polega na wywo$aniu metody
obiektu subskrybenta. Oznacza to, &e w momencie zg$oszenia ch#ci otrzymywania powia-
domie+ subskrybent musi przekaza* obiektowi

paper

jedn% ze swoich metod w wywo$aniu

metody

subscribe()

.

Obiekt

paper

mo&e dodatkowo umo&liwi* anulowanie subskrypcji, czyli usuni#cie wpisu

z tablicy subskrybentów. Ostatni% istotn% metod% obiektu

paper

jest

publish()

, która wy-

wo$uje metody subskrybentów. Podsumowuj%c, obiekt publikuj%cy musi zawiera* nast#puj%ce
sk$adowe:

"

subscribers

— tablica;

"

subscribe()

— dodaje wpis do tablicy;

"

unsubscribe()

— usuwa wpis z tablicy;

"

publish()

— przechodzi w p#tli przez subskrybentów i wywo$uje przekazane przez

nich metody.

Wszystkie trzy metody potrzebuj% parametru

type

, poniewa& wydawca mo&e zg$osi* kilka

ró&nych zdarze+ (publikacj# gazety lub magazynu), a subskrybenci mog% zdecydowa* si# na
otrzymywanie powiadomie+ tylko o jednym z nich.

Poniewa& powy&sze sk$adowe s% bardzo ogólne i mog% by* stosowane przez dowolnego
wydawc#, warto zaimplementowa* je jako cz#'* osobnego obiektu. W ten sposób b#dzie je
mo&na w przysz$o'ci skopiowa* do dowolnego obiektu, zamieniaj%c go w wydawc# (obiekt
publikuj%cy).

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

164

Rozdzia$ 7. Wzorce projektowe

Oto przyk$adowa implementacja ogólnej funkcjonalno'ci obiektu publikuj%cego, która defi-
niuje wszystkie wymagane sk$adowe oraz metod# pomocnicz%

visitSubscribers()

:

var publisher = {
subscribers: {
any: [] // typ zdarzenia
},
subscribe: function (fn, type) {
type = type || 'any';
if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn);
},
unsubscribe: function (fn, type) {
this.visitSubscribers('unsubscribe', fn, type);
},
publish: function (publication, type) {
this.visitSubscribers('publish', publication, type);
},
visitSubscribers: function (action, arg, type) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers.length;

for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i](arg);
} else {
if (subscribers[i] === arg) {
subscribers.splice(i, 1);
}
}
}
}
};

Poni&ej znajduje si# kod funkcji, która przyjmuje obiekt i zamienia go w obiekt publikuj%cy
przez proste skopiowanie wszystkich ogólnych metod dotycz%cych publikacji.

function makePublisher(o) {
var i;
for (i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] === "function") {
o[i] = publisher[i];
}
}
o.subscribers = {any: []};
}

Czas na implementacj# obiektu

paper

, który b#dzie publikowa$ gazet# i magazyn.

var paper = {
daily: function () {
this.publish("ciekawy news");
},
monthly: function () {
this.publish("interesuj^c^ analiz~", "magazyn");
}
};

Trzeba jeszcze uczyni* z obiektu wydawc#.

makePublisher(paper);

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Obserwator

165

Po utworzeniu wydawcy mo&emy utworzy* obiekt subskrybenta o nazwie

joe

, który ma

dwie metody.

var joe = {
drinkCoffee: function (paper) {
console.log('W_a,nie przeczyta_em ' + paper);
},
sundayPreNap: function (monthly) {
console.log('Chyba zasn~, czytaj^c ' + monthly);
}
};

Nast#pny krok to obiekt

paper

subskrybuj%cy

joe

(tak naprawd# to

joe

zg$asza si# jako sub-

skrybent do

paper

).

paper.subscribe(joe.drinkCoffee);
paper.subscribe(joe.sundayPreNap, 'magazyn');

Obiekt

joe

udost#pni$ dwie metody. Pierwsza z nich powinna by* wywo$ywana dla domy'lnego

zdarzenia „wszystko”, a druga jedynie dla zdarze+ „magazyn”. Oto kilka zg$osze+ zdarze+:

paper.daily();
paper.daily();
paper.daily();
paper.monthly();

Wszystkie metody publikuj%ce wywo$a$y odpowiednie metody z obiektu

joe

, co spowodo-

wa$o wy'wietlenie w konsoli nast#puj%cego wyniku:

W_a,nie przeczyta_em ciekawy news
W_a,nie przeczyta_em ciekawy news
W_a,nie przeczyta_em ciekawy news
Chyba zasn~, czytaj^c interesuj^c^ analiz~

Bardzo wa&nym elementem ca$ego systemu jest to, &e

paper

nie zawiera w sobie informacji

o

joe

i odwrotnie. Co wi#cej, nie istnieje obiekt mediatora, który wiedzia$by o wszystkich

obiektach. Obiekty uczestnicz%ce w interakcjach s% ze sob% powi%zane bardzo lu;no i bez ja-
kichkolwiek modyfikacji mo&na doda* jeszcze kilku subskrybentów. Co wa&ne,

joe

mo&e

w dowolnym momencie anulowa* subskrypcj#.

Nic te& nie stoi na przeszkodzie, by

joe

równie& zosta$ wydawc% (przecie& to nic trudnego

dzi#ki systemom blogowym i mikroblogowym). Jako wydawca

joe

wysy$a aktualizacj# swojego

statusu do serwisu Twitter:

makePublisher(joe);
joe.tweet = function (msg) {
this.publish(msg);
};

Wyobra;my sobie, &e dzia$ relacji z klientami wydawcy gazety decyduje si# czyta*, co o ga-
zecie s%dzi jej subskrybent

joe

, i dodaje w tym celu metod#

readTweets()

.

paper.readTweets = function (tweet) {
alert('Zwo_ajmy du`e zebranie! Kto, napisa_: ' + tweet);
};
joe.subscribe(paper.readTweets);

Gdy

joe

zamie'ci swój wpis, wydawca (

paper

) go otrzyma.

joe.tweet("nie lubi~ tej gazety");

Wykonanie kodu spowoduje wy'wietlenie w konsoli tekstu „Zwo$ajmy du&e zebranie! Kto'
napisa$: nie lubi# tej gazety”.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

166

Rozdzia$ 7. Wzorce projektowe

Pe$ny kod ;ród$owy przyk$adu oraz mo&liwo'* sprawdzenia wyników jego dzia$ania w konsoli
zapewnia strona HTML dost#pna pod adresem http://www.jspatterns.com/book/7/observer.html.

Drugi przyk$ad — gra w naciskanie klawiszy

Przyjrzymy si# jeszcze jednemu przyk$adowi. Zaimplementujemy t# sam% gr# w naciskanie
klawiszy co przy wzorcu mediatora, ale tym razem u&yjemy wzorca obserwatora. Aby nieco
utrudni* zadanie, zapewnijmy obs$ug# dowolnej liczby graczy, a nie tylko dwóch. Ponownie
skorzystamy z konstruktora

Player()

, który tworzy obiekty graczy, i z obiektu

scoreboard

.

Jedynie obiekt

mediator

zamieni si# w obiekt

game

.

We wzorcu mediatora obiekt

mediator

wiedzia$ o wszystkich uczestnikach gry i wywo$ywa$

ich metody. Obiekt

game

ze wzorca obserwatora nie b#dzie tego robi$ — to same obiekty b#d%

zg$asza$y ch#* otrzymywania informacji o zaj'ciu wybranych zdarze+. Przyk$adowo, obiekt

scoreboard

zg$osi ch#* bycia informowanym o zaj'ciu zdarzenia

scorechange

w obiekcie

game

.

Oryginalny obiekt

publisher

nale&y nieco zmieni*, by upodobni* go do rozwi%za+ znanych

z przegl%darek internetowych.

"

Zamiast metod

publish()

,

subscribe()

i

unsubscribe()

pojawi% si# metody

fire()

,

on()

i

remove()

.

"

Typ zdarzenia (

type

) b#dzie u&ywany ca$y czas, wi#c stanie si# pierwszym parametrem

wszystkich trzech funkcji.

"

Dodatkowy parametr

context

przekazywany wraz z funkcj% powiadomienia umo&liwi

wywo$anie funkcji zwrotnej z odpowiednio ustawion% warto'ci%

this

.

Nowy obiekt

publisher

ma nast#puj%c% posta*:

var publisher = {
subscribers: {
any: []
},
on: function (type, fn, context) {
type = type || 'any';
fn = typeof fn === "function" ? fn : context[fn];

if (typeof this.subscribers[type] === "undefined") {
this.subscribers[type] = [];
}
this.subscribers[type].push({fn: fn, context: context || this});
},
remove: function (type, fn, context) {
this.visitSubscribers('unsubscribe', type, fn, context);
},
fire: function (type, publication) {
this.visitSubscribers('publish', type, publication);
},
visitSubscribers: function (action, type, arg, context) {
var pubtype = type || 'any',
subscribers = this.subscribers[pubtype],
i,
max = subscribers ? subscribers.length : 0;

for (i = 0; i < max; i += 1) {
if (action === 'publish') {
subscribers[i].fn.call(subscribers[i].context, arg);
} else {

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Obserwator

167

if (subscribers[i].fn === arg && subscribers[i].context === context) {
subscribers.splice(i, 1);
}
}
}
}
};

Nowy konstruktor

Player()

wygl%da nast#puj%co:

function Player(name, key) {
this.points = 0;
this.name = name;
this.key = key;
this.fire('newplayer', this);
}

Player.prototype.play = function () {
this.points += 1;
this.fire('play', this);
};

Nowym parametrem przyjmowanym przez konstruktor jest

key

— okre'la on klawisz na

klawiaturze, który gracz b#dzie naciska$, by uzyskiwa* punkty (we wcze'niejszej wersji kodu
klawisze by$y zapisane na sztywno). Dodatkowo utworzenie nowego obiektu gracza powo-
duje zg$oszenie zdarzenia

newplayer

, a ka&de naci'ni#cie klawisza przez gracza skutkuje

zg$oszeniem zdarzenia

play

.

Obiekt

scoreboard

pozostaje bez zmian — nadal aktualizuje tablic# wyników, korzystaj%c

z bie&%cych warto'ci.

Nowy obiekt

game

potrafi 'ledzi* poczynania wszystkich graczy, by móg$ zlicza* wyniki

i zg$asza* zdarzenie

scorechange

. Dodatkowo zg$asza si# on jako subskrybent wszystkich

zdarze+

keypress

przegl%darki, by wiedzie* o wszystkich klawiszach przypisanych poszcze-

gólnym graczom.

var game = {

keys: {},

addPlayer: function (player) {
var key = player.key.toString().charCodeAt(0);
this.keys[key] = player;
},

handleKeypress: function (e) {
e = e || window.event; // IE
if (game.keys[e.which]) {
game.keys[e.which].play();
}
},

handlePlay: function (player) {
var i,
players = this.keys,
score = {};

for (i in players) {
if (players.hasOwnProperty(i)) {
score[players[i].name] = players[i].points;
}

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

168

Rozdzia$ 7. Wzorce projektowe

}
this.fire('scorechange', score);
}
};

Funkcja

makePublisher()

, która zamienia$a dowolny obiekt w obiekt publikuj%cy zdarzenia,

jest identyczna jak w przyk$adzie z wydawc% i gazet%. Obiekt

game

b#dzie zg$asza$ zdarzenia

takie jak

scorechange

. Obiektem publikuj%cym stanie si# równie&

Player.prototype

, by mo&li-

we by$o zg$aszanie zdarze+

play

i

newplayer

wszystkim zainteresowanym.

makePublisher(Player.prototype);
makePublisher(game);

Obiekt

game

zg$asza si# jako subskrybent zdarze+

play

i

newplayer

(a tak&e zdarzenia

keypress

przegl%darki), natomiast obiekt

scoreboard

chce by* powiadamiany o zdarzeniach

scorechange

.

Player.prototype.on("newplayer", "addPlayer", game);
Player.prototype.on("play", "handlePlay", game);
game.on("scorechange", scoreboard.update, scoreboard);
window.onkeypress = game.handleKeypress;

Metoda

on()

umo&liwia subskrybentom okre'lenie funkcji zwrotnej jako referencji (

score

board.update

) lub jako tekstu (

"addPlayer"

). Wersja tekstowa dzia$a prawid$owo tylko

w przypadku przekazania jako trzeciego parametru kontekstu (na przyk$ad

game

).

Ostatni element to dynamiczne tworzenie tylu obiektów graczy (po naci'ni#ciu klawiszy), ile
zostanie za&%danych przez graj%cych.

var playername, key;
while (1) {
playername = prompt("Dodaj gracza (imi~)");
if (!playername) {
break;
}
while (1) {
key = prompt("Klawisz dla gracza " + playername + "?");
if (key) {
break;
}
}
new Player(playername, key);
}

To ju& wszystko w temacie gry. Pe$ny kod ;ród$owy wraz z mo&liwo'ci% zagrania znajduje
si# pod adresem http://www.jspatterns.com/book/7/observer-game.html.

W implementacji wzorca mediatora obiekt

mediator

musia$ wiedzie* o wszystkich obiektach,

by móc w odpowiednim czasie wywo$ywa* w$a'ciwe metody i koordynowa* ca$% gr#. W nowej
implementacji obiekt

game

jest nieco g$upszy i wykorzystuje fakt, i& obiekty zg$aszaj% zdarzenia

i obserwuj% si# nawzajem (na przyk$ad obiekt

scoreboard

nas$uchuje zdarzenia

scorechange

).

Zapewnia to jeszcze lu;niejsze powi%zanie obiektów (im mniej z nich wie o innych, tym lepiej),
cho* za cen# utrudnionej analizy, kto tak naprawd# nas$uchuje kogo. W przyk$adowej grze
wszystkie subskrypcje s% na razie w jednym miejscu, ale gdyby sta$a si# ona bardziej rozbu-
dowana, wywo$ania

on()

mog$yby si# znale;* w wielu ró&nych miejscach (niekoniecznie

w kodzie inicjalizuj%cym). Taki kod trudniej jest testowa*, gdy& trudno od razu zrozumie*,
co tak naprawd# si# w nim dzieje. Wzorzec obserwatora zrywa ze standardowym, proce-
duralnym wykonywaniem kodu od pocz%tku do ko+ca.

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Podsumowanie

169

Podsumowanie

W rozdziale pojawi$y si# opisy kilku popularnych wzorców projektowych i przyk$ady ich
implementacji w j#zyku JavaScript. Omawiane by$y nast#puj%ce wzorce:

"

Singleton

— tworzenie tylko jednego obiektu danej „klasy”. Pojawi$o si# kilka rozwi%-

za+, w tym takie, które stara$y si# zachowa* sk$adni# znan% z j#zyka Java przez zastoso-
wanie funkcji konstruuj%cych. Trzeba jednak pami#ta*, &e z technicznego punktu widze-
nia w j#zyku JavaScript wszystkie obiekty s% singletonami. Nie nale&y te& zapomina*, &e
czasem programi'ci stosuj% s$owo „singleton”, a maj% na my'li obiekty utworzone przy
u&yciu wzorca modu$u.

"

Fabryka

— metoda tworz%ca obiekty o typie przekazanym jako warto'* tekstowa.

"

Iterator

— interfejs umo&liwiaj%cy $atwe przetwarzanie elementów umieszczonych w z$o-

&onej strukturze danych.

"

Dekorator

— modyfikacja obiektów w trakcie dzia$ania programu przez dodawanie do

nich funkcjonalno'ci zdefiniowanych w dekoratorach.

"

Strategia

— zachowanie tego samego interfejsu przy jednoczesnym wyborze najlepszej

strategii jego implementacji (uzale&nionej od kontekstu).

"

Fasada

— zapewnienie bardziej przyjaznego API przez otoczenie typowych (lub ;le za-

projektowanych) metod ich nowymi wersjami.

"

Po1rednik

— otoczenie obiektu zapewniaj%ce kontrol# dost#pu do niego, gdy celem jest

unikni#cie wykonywania kosztownych operacji przez ich grupowanie lub opó;nianie do
momentu, gdy b#d% naprawd# konieczne.

"

Mediator

— promowanie lu;nego powi%zania obiektów przez unikanie bezpo'redniej

komunikacji mi#dzy nimi i zast%pienie jej komunikacj% poprzez obiekt mediatora.

"

Obserwator

— lu;ne powi%zanie mi#dzy obiektami osi%gane przez tworzenie obiektów,

których zmiany mo&na obserwowa*, jawnie zg$aszaj%c si# jako subskrybent (cz#sto mówi
si# równie& o w$asnych zdarzeniach lub wzorcu subskrybenta-dostawcy).

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

170

Rozdzia$ 7. Wzorce projektowe

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

195

Skorowidz

.htaccess, 185
@class, 43
@method, 43
@namespace, 43
@param, 41, 43
@return, 41, 43
<script>, 186, 187

dodawanie elementu, 190
dynamiczne elementy, 189
lokalizacja, 187

A

addEventListener(), 175
alert(), 20
antywzorce, 16
Apache, .htaccess, 185
aplikacja

cz#'ciowa, 85, 86, 89
funkcji, 84, 85
internetowa, 171

apply(), 85, 133, 134
arguments.callee, 55, 83
Array, 56, 57
asynchroniczne, zdarzenia, 73
atrybut, 17
attachEvent(), 175

B

b%belkowanie zdarze+, 177
bind(), 135, 136
break, 32

C

call(), 133, 134
case, 32
CDN, 186
Closure Compiler, 46, 80
console, obiekt, 20

constructor, w$a'ciwo'*, 18, 126
Content Delivery Network, Patrz CDN
Crockford, Douglas, 19, 113
Curry, Haskell, 87
currying, Patrz funkcje, rozwijanie

D

default, 32
dekoratora, wzorzec, 145, 169

implementacja, 146, 147, 148, 149

delegacje zdarze+, wzorzec, 177
delete, operator, 24
dir(), 20
Document Object Model, Patrz DOM
dodatki syntaktyczne, 113
dokumentacja, 41

JSDoc, 41
YUIDoc, 41, 42, 44

DOM, 172

dost#p, 173
modyfikacja, 174

dorozumiane zmienne globalne, 23, 24
dziedziczenie, 18, 136

klasyczne, 115, 116, 126
nowoczesne, 115, 116
prototypowe, 129, 130
przez kopiowanie w$a'ciwo'ci, 131, 132
wielobazowe, 121

E

ECMAScript 5, 18, 19

dodatki, 130

Error(), 62
ES5, Patrz ECMAScript 5
eval(), 19

unikanie, 32, 33

Expires, nag$ówek, 185
extend(), 97, 132
extendDeep(), 97

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

196

Skorowidz

F

fabryki, wzorzec, 141, 142, 143, 169
fasady, wzorzec, 152, 153, 169
Firebug, 132
for, p#tla, 27, 28
for-in, p#tla, 29
Function(), 33, 66
Function.prototype.apply(), 84
funkcje, 17, 65, 66

anonimowe, 66, 68
czasowe, 73
deklaracje, 67, 68
konstruuj%ce, 51, 52
name, w$a'ciwo'*, 68
natychmiastowe, 76, 77, 78, 79, 89
obs$ugi zdarze+ asynchronicznych, 73
po'rednicz%ce, 126
prywatne, 99
rozwijanie, 84, 86, 87, 89
samodefiniuj%ce si#, 75, 76, 90
samowywo$uj%ce si#, 79
terminologia, 66
w$a'ciwo'ci, 82
wywo$ania zwrotnego, 70
wywo$anie, 85
zwracanie, 74, 89

G

globalne zmienne, 22, 23, 24

dorozumiane, 23, 24

gospodarza, obiekty, 17, 18

H

hasOwnProperty(), 29, 30
hoisting, Patrz przenoszenie deklaracji
HTML, wysy$anie pliku fragmentami, 188, 189
HTMLCollection, 27, 28

I

inicjalizacja, 25

leniwa, 153

init(), 79, 80
instanceof, operator, 108
instancja, 115
isArray(), 57
iteratora, wzorzec, 143, 144, 169

J

JavaScript, 15

biblioteki, 94
jako j#zyk obiektowy, 16
sprawdzanie jako'ci kodu, 19
'rodowisko uruchomieniowe, 18

JavaScript Object Notation, Patrz JSON
jQuery, biblioteka, 59, 132
JSDoc, 41
JSLint, 19, 47
JSON, 58
JSON with Padding, Patrz JSONP
JSON.parse(), 58, 59
JSON.stringify(), 59
JSONP, 181, 182, 183

K

klasy, 17, 126

emulacja, 126, 127

kod

konwencje, 34, 35, 36, 37, 38
$atwy w konserwacji, 21, 22
minifikowanie, 46
ocenianie przez innych, 45, 46
usuwanie warunkowych wersji, 80, 81, 90
wielokrotne u&ycie, 115

kodowania, wzorce, 16
komentarze, 40, 41
kompresja, 185
konsola, 20
konstruktory, 54, 119

czyszczenie referencji, 125
po'rednicz%ce, 126
po&yczanie, 119, 121, 122
samowywo$uj%ce, 55
tymczasowe, 124, 126
warto'* zwracana, 53

konwencje kodu, 34, 35

bia$e spacje, 37, 38
nawias otwieraj%cy, 36, 37
nawiasy klamrowe, 35, 36
nazewnictwo, 38, 39, 40, 54
'redniki, 37
wci#cia, 35

konwersja liczb, 34
kopia

g$#boka, 131
p$ytka, 131

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Skorowidz

197

L

leniwa inicjalizacja, 153
leniwe wczytywanie, 190, 191
liczby, konwersja, 34
litera$y

funkcji, 67
obiektów, 49, 50, 51, 98
tablicy, 56
wyra&enia regularnego, 59, 60

log(), 20
lokalne zmienne, 22

+

$a+cuchy wywo$a+, 112

M

Martin, Robert, 112
mediator, 160
mediatora, wzorzec, 160, 169

przyk$ad, 160, 161, 162
uczestnicy, 160

method(), 113
metody, 17, 49

po&yczanie, 133, 134
prywatne, 95, 96
publiczne, 99
statyczne, 107, 108
uprzywilejowane, 96

minifikacja, 46, 185
modu$y, 100, 101, 102

import zmiennych globalnych, 103
tworz%ce konstruktory, 102

N

najmniejszego przywileju, zasada, 97
name, w$a'ciwo'*, 68
natychmiastowa inicjalizacja obiektu, 79, 90
nazewnictwo, konwencje, 38, 39, 40, 54
nazwane wyra&enie funkcyjne, 66, 67
new, s$owo kluczowe, 54, 138
nienazwane wyra&enie funkcyjne, 66, 68
notacja litera$u obiektu, 49, 50, 51

O

obiekty, 17, 51

b$#dów, 62
globalne, 22, 25
gospodarza, 17, 18

konfiguracyjne, 83, 84, 89
natychmiastowa inicjalizacja, 79, 90
rdzenne, 17
tworzenie, 51, 91

Object(), 18, 51, 143
Object.create(), 130
Object.prototype.toString(), 58
obserwator, 163
obserwatora, wzorzec, 163, 166, 169
obs$uga zdarze+, 175, 176

asynchronicznych, 73

onclick, atrybut, 175
open(), 180

P

parseInt(), 34
parseJSON(), 59
p#tle

for, 27, 28
for-in, 29

piaskownicy, wzorzec, 103, 104, 105, 114

dodawanie modu$ów, 105
globalny konstruktor, 104
implementacja konstruktora, 106

po'rednika, wzorzec, 153, 155, 158, 159, 169
preventDefault(), 152
projektowe, wzorce, 16
prototype, w$a'ciwo'*, 18, 98

modyfikacja, 31

prototypy, 18

$a+cuch, 117, 118, 120, 121
modyfikacja, 31
prywatno'*, 98
wspó$dzielenie, 123, 124

prywatno'*, problemy, 96
przegl%darki, wykrywanie, 194
przenoszenie deklaracji, 26, 27
przestrzenie nazw, 22, 91, 92, 114

Firebug, 94

R

ramki, 184
rdzenne obiekty, 17
RegExp(), 59, 60
rzutowanie niejawne, 32

S

Schönfinkel, Moses, 87
schönfinkelizacja, 87
send(), 180

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ

background image

Czytaj dalej...

198

Skorowidz

serializacja, 82, 83
serwer, komunikacja, 179
setInterval(), 33, 73
setTimeout(), 33, 73, 178
singleton, 137, 138, 169
sk$adowe

prywatne, 96
statyczne, 107, 109

skrypty

$%czenie, 184, 185
obliczeniowe, 179
strategie wczytywania, 186

sta$e, 110, 111
stopPropagation(), 152
strategii, wzorzec, 149, 169
strict mode, Patrz tryb 'cis$y
String.prototype.replace(), 60
styl wielb$%dzi, 39
subskrybenta-dostawcy, wzorzec, 163, 169
supermetody, 152
switch, 31, 32
SyntaxError(), 62

K

'rodowisko uruchomieniowe, 18

T

that, 54, 55
this, 22, 53
throw, instrukcja, 62
tryb 'cis$y, 19
TypeError(), 62
typeof, 32, 57
typy proste, otoczki, 61, 62

V

var, 23

efekty uboczne pomini#cia, 24
problem rozrzuconych deklaracji, 26
wzorzec pojedynczego u&ycia, 25

W

walidacja danych, 150
w%tki, symulacja, 178
wczytywanie

leniwe, 190, 191
na &%danie, 191
wst#pne, 192, 193, 194

wielb$%dzi, styl, 39
window, w$a'ciwo'*, 22, 25
with, polecenie, 19
w$a'ciwo'ci, 17, 49

prywatne, 95, 96
statyczne, 107, 110

wydajno'*, 184
wyliczenie, 29
wyra&enia regularne, 59
wyra&enie funkcyjne, 66, 67

nazwane, 66, 67
nienazwane, 66

wywo$anie funkcji, 85
wywo$anie jako obraz, 184
wywo$anie zwrotne, 70, 71, 89

w bibliotekach, 74
zakres zmiennych, 72

wzorce, 11, 15

antywzorce, 16
API, 89
inicjalizacyjne, 89
kodowania, 16
optymalizacyjne, 90
projektowe, 16

X

XHR, Patrz XMLHttpRequest
XMLHttpRequest, 180, 181

Y

Y.clone(), 132
Y.delegate(), 178
Yahoo! Query Language, Patrz YQL
YQL, 157
YUI3, 132, 178
YUIDoc, 41, 42, 44

przyk$ad dokumentacji, 42, 44

Z

zdarzenia, 175

asynchroniczne, 73
delegacje, 177
obs$uga, 175, 176
w$asne, 163

zmienne, 17

globalne, 22, 23, 24, 103
lokalne, 22

Kup ksiąĪkĊ

Pole

ü ksiąĪkĊ


Wyszukiwarka

Podobne podstrony:

więcej podobnych podstron