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

 

 

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

 

 

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()

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 

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

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Ċ