background image

JavaScript - mocne strony

Autor: Douglas Crockford
ISBN: 978-83-246-1998-6
Tytu³ orygina³u: 

JavaScript: The Good Parts

Format: 168x237, stron: 160

Poznaj doskona³¹ u¿ytecznoœæ jêzyka JavaScript!

• 

Jak efektywnie wykorzystaæ najlepsze funkcje JavaScript?

• 

Jak pisaæ programy, aby ustrzec siê b³êdów?

• 

Jak zdefiniowaæ podzbiór jêzyka i tworzyæ idealne aplikacje?

Warto poznaæ jêzyk JavaScript, poniewa¿ stanowi on jedno z wa¿niejszych narzêdzi
w informatyce — dziêki temu, ¿e jest jednoczeœnie podstawowym i domyœlnym jêzykiem 
przegl¹darek internetowych oraz jêzykiem programowania. JavaScript pozwala na 
tworzenie wydajnego kodu bibliotek obiektowych czy aplikacji opartych na technice 
AJAX. Jego skrypty s³u¿¹ najczêœciej do zapewniania interaktywnoœci, sprawdzania 
poprawnoœci formularzy oraz budowania elementów nawigacyjnych. Doœæ ³atwa 
sk³adnia sprawia, ¿e pisanie pe³noprawnych i wydajnych aplikacji w tym jêzyku nie jest 
trudne nawet dla pocz¹tkuj¹cych programistów. 

Ksi¹¿ka „JavaScript — mocne strony” to wyj¹tkowy podrêcznik do nauki tego 
popularnego, dynamicznego jêzyka programowania. Dowiesz siê z niej, jak efektywnie 
wykorzystaæ wszystkie jego mocne strony (m.in. funkcje, dynamiczne obiekty, litera³y 
obiektowe) oraz jak unikaæ pu³apek. Poznasz elementy sk³adowe jêzyka oraz sposoby 
ich ³¹czenia, zrozumiesz, na czym polega dziedziczenie prototypowe, w jaki sposób 
brak kontroli typów ma pozytywny wp³yw na pisanie aplikacji oraz dlaczego stosowanie 
zmiennych globalnych jako podstawowego modelu programowania nie jest dobrym 
pomys³em. Znaj¹c wszelkie ograniczenia jêzyka JavaScript, bêdziesz móg³ 
profesjonalnie wykorzystaæ jego najlepsze czêœci.

• 

Gramatyka jêzyka JavaScript

• 

Obiekty i funkcje

• 

Rekurencja

• 

Kaskadowe ³¹czenie wywo³añ

• 

Litera³y obiektowe

• 

Dziedziczenie — pseudoklasyczne, prototypowe, funkcyjne

• 

Tablice

• 

Wyra¿enia regularne

• 

Klasa znaków i kwantyfikator wyra¿enia regularnego

Nie traæ czasu — siêgaj tylko po to, co najlepsze w jêzyku JavaScript! 

  

background image

5

Spis tre

ļci

Wst

ýp ........................................................................................................................................9

1.  Mocne strony  ................................................................................................................11

Dlaczego JavaScript?

12

Analizujñc JavaScript

12

Prosta platforma testowa

14

2.  Gramatyka  ................................................................................................................... 15

Biaäe znaki

15

Nazwy

16

Liczby

17

ãaþcuchy znakowe

18

Instrukcje

20

WyraĔenia

24

Literaäy

27

Funkcje

28

3.  Obiekty .........................................................................................................................29

Literaäy obiektowe

29

Pobieranie

30

Modyfikacja

30

Referencja

31

Prototyp

31

Refleksja

32

Wyliczanie

32

Usuwanie

33

Ograniczanie liczby zmiennych globalnych

33

4.  Funkcje  .........................................................................................................................35

Obiekty funkcji

35

Literaä funkcji

36

Wywoäanie

36

Argumenty

39

background image

6

_

Spis tre

ļci

Powrót z funkcji

40

Wyjñtki

40

Rozszerzanie typów

41

Rekurencja

42

Zasiög

43

Domkniöcia

44

Wywoäania zwrotne

47

Moduä

47

Kaskadowe äñczenie wywoäaþ

49

Funkcja curry

50

Spamiötywanie

51

5.  Dziedziczenie ...............................................................................................................53

Dziedziczenie pseudoklasyczne

53

OkreĈlenia obiektów

56

Dziedziczenie prototypowe

56

Dziedziczenie funkcyjne

58

CzöĈci

61

6.  Tablice  ..........................................................................................................................63

Literaäy tablicowe

63

DäugoĈè tablicy

64

Usuwanie elementów

65

Wyliczanie

65

Problem z rozpoznawaniem typu

65

Metody

66

Wymiary

67

7.  Wyra

Ŝenia regularne ...................................................................................................69

Przykäad

70

Tworzenie

74

Elementy

75

8.  Metody  ......................................................................................................................... 81

9.  Styl ................................................................................................................................ 97

10.  Najpi

ýkniejsze cechy jýzyka .......................................................................................101

background image

Spis tre

ļci

_

7

Dodatek A K

ĥopotliwe cechy jýzyka .................................................................................... 105

Dodatek B Nietrafione cechy j

ýzyka .....................................................................................113

Dodatek C JSLint  ....................................................................................................................119

Dodatek D Diagramy sk

ĥadni  ............................................................................................... 129

Dodatek E JSON  .................................................................................................................... 139

Skorowidz ............................................................................................................................. 149

background image

53

ROZDZIA

Ĥ 5.

Dziedziczenie

Dziedziczenie jest waĔnym zagadnieniem w wiökszoĈci jözyków programowania.

W jözykach klasycznych (takich jak Java) dziedziczenie ma dwa gäówne zadania. Po pierwsze,
jest formñ wielokrotnego uĔycia kodu. JeĈli nowa klasa jest podobna do istniejñcej, wystarczy
okreĈliè dzielñce je róĔnice. Ponowne wykorzystanie kodu jest waĔne, poniewaĔ pozwala zmniej-
szyè koszty wytwarzania oprogramowania. Drugñ zaletñ dziedziczenia jest to, Ĕe zawiera ono
w sobie specyfikacjö systemu typów. Uwalnia to programistów w duĔej mierze od koniecznoĈci
rzutowania  z  jednego  typu  na  drugi,  co  jest  istotnñ  zaletñ,  poniewaĔ  rzutowanie  podwaĔa
caäñ wartoĈè bezpieczeþstwa systemu typów.

JavaScript, jako jözyk bez kontroli typów, nigdy nie wymaga rzutowania. Hierarchia dziedzi-
czenia obiektu nie ma tu znaczenia. WaĔne jest, co obiekty potrafiñ robiè, a nie po czym
dziedziczñ.

JavaScript posiada duĔo bogatszy zasób moĔliwoĈci ponownego wykorzystania kodu. MoĔe
naĈladowaè  klasyczne  wzorce,  ale  dostarcza  równieĔ  innych,  bardziej  ekspresyjnych.  Zbiór
moĔliwych wzorców dziedziczenia w JavaScripcie jest bardzo szeroki. W tym rozdziale przyj-
rzymy siö kilku najprostszym przypadkom. DuĔo bardziej skomplikowane sñ równieĔ moĔ-
liwe, ale zazwyczaj lepiej jest trzymaè siö tych najprostszych.

W klasycznych jözykach obiekty sñ instancjami klas, a klasa moĔe dziedziczyè po innej klasie.
JavaScript jest jözykiem prototypowym, co oznacza, Ĕe obiekty dziedziczñ bezpoĈrednio z in-
nych obiektów.

Dziedziczenie pseudoklasyczne

Jözyk JavaScript ma wewnötrznie gäöboko rozdartñ naturö. Jego mechanizm prototypowy jest
zaciemniany przez niektóre skomplikowane elementy skäadni, które wyglñdajñ bardziej kla-
sycznie. Zamiast pozwoliè obiektom dziedziczyè bezpoĈrednio z innych obiektów, JavaScript
wprowadza  niepotrzebny  poziom  abstrakcji,  w  którym  obiekty  tworzone  sñ  przy  uĔyciu
funkcji konstruktorów.

Kiedy tworzony jest obiekt funkcji, konstruktor 

Function

 zwracajñcy obiekt funkcji wykonuje

kod podobny do poniĔszego:

this.prototype = {constructor: this};

background image

54

_

Rozdzia

ĥ 5. Dziedziczenie

Nowy obiekt funkcji otrzymuje wäaĈciwoĈè 

prototype

, której wartoĈciñ jest obiekt posiadajñcy

wäaĈciwoĈè 

constructor

, której to z kolei wartoĈciñ jest nowy obiekt funkcji. Obiekt 

prototype

jest  miejscem,  gdzie  zäoĔone  majñ  byè  odziedziczone  wäaĈciwoĈci.  KaĔda  funkcja  otrzymuje
obiekt 

prototype

, poniewaĔ jözyk nie posiada sposobu okreĈlenia, które funkcje sñ przezna-

czone do uĔycia w roli konstruktorów. WäaĈciwoĈè 

constructor

 nie jest zbyt uĔyteczna. To

obiekt 

prototype

 ma znaczenie.

Kiedy  funkcja  jest  wywoäywana  wedäug  wzorca  wywoäania  konstruktora  z  uĔyciem  säowa

new

, zmienia siö sposób wykonania funkcji. Gdyby operator 

new

 byä metodñ, a nie operatorem,

mógäby byè zaimplementowany w ten sposób:

Function.method('new', function () {

   // Tworzymy nowy obiekt dziedzicz

ący z prototypu konstruktora.

   var that = Object.beget(this.prototype);

   // Wywo

áujemy konstruktor, wiąĪąc this do nowego obiektu.

   var other = this.apply(that, arguments);

   // Je

Ğli zwracana wartoĞü nie jest obiektem,

   // podmie

Ĕ ją na nowo utworzony obiekt.

   return (typeof other === 'object' && other) || that;
});

MoĔemy zdefiniowaè konstruktor i rozszerzyè jego prototyp:

var Mammal = function (name) {
   this.name = name;
};

Mammal.prototype.get_name = function () {
   return this.name;
};

Mammal.prototype.says = function () {
   return this.saying || '';
};

Tworzymy instancjö:

var myMammal = new Mammal('Mój ssak');
var name = myMammal.get_name(); // 'Mój ssak'

Nastöpnie moĔemy utworzyè innñ pseudoklasö dziedziczñcñ z 

Mammal

, definiujñc jej konstruktor

i zastöpujñc jej prototyp instancjñ 

Mammal

:

var Cat = function (name) {
   this.name = name;
   this.saying = 'miau';
};

// Zast

Ċpujemy Cat.prototype instancją Mammal

Cat.prototype = new Mammal();

// Rozszerzamy nowy prototyp metodami purr i get_name

Cat.prototype.purr = function (n) {
   var i, s = '';
   for (i = 0; i < n; i += 1) {

background image

Dziedziczenie pseudoklasyczne

_

55

      if (s) {
         s += '-';
      }
      s += 'r';
   }
   return s;
};
Cat.prototype.get_name = function () {
      return this.says() + ' ' + this.name + ' ' + this.says();
};

var myCat = new Cat('Kicia');
var says = myCat.says();  // 'miau'
var purr = myCat.purr(5);  // 'r-r-r-r-r'
var name = myCat.get_name();  // 'miau Kicia miau'

Pseudoklasyczne dziedziczenie miaäo w zamiarach wyglñdaè bardziej obiektowo, lecz w prak-
tyce wyglñda dziwnie i obco. CzöĈè brzydoty tego rozwiñzania moĔemy ukryè, uĔywajñc metody

method

 i definiujñc metodö 

inherits

:

Function.method('inherits', function (Parent) {
   this.prototype = new Parent();
   return this;
});

Obie te metody zwracajñ 

this

, wiöc moĔemy je wykorzystaè przy äñczeniu wywoäaþ. MoĔe-

my teraz utworzyè nasz obiekt 

Cat

 za pomocñ jednej instrukcji

1

:

var Cat = function (name) {
   this.name = name;
   this.saying = 'miau';
}.
   inherits(Mammal).
   method('purr', function (n) {
      var i, s = '';
      for (i = 0; i < n; i += 1) {
         if (s) {
            s += '-';
         }
         s += 'r';
      }
      return s;
   }).
   method('get_name', function () {
      return this.says() + ' ' + this.name + ' ' + this.says();
   });

Dziöki  ukryciu  caäego  mechanizmu  operujñcego  na  obiekcie 

prototype

,  ten  kod  wyglñda

nieco  mniej  obco.  Ale  czy  naprawdö  coĈ  ulepszyliĈmy?  Mamy  teraz  funkcje  konstruktorów
przypominajñce klasy, ale pod tñ powierzchniñ wciñĔ moĔemy siö natknñè na nieprzewidziane
zachowanie. Nie mamy prywatnego dostöpu — wszystkie wäaĈciwoĈci sñ publiczne. Nie mamy
moĔliwoĈci wywoäywania nieprzesäoniötych wersji metod obiektów nadrzödnych z poziomu
obiektów przesäaniajñcych te metody.

Co gorsza, istnieje pewne ryzyko zwiñzane z uĔyciem funkcji konstruktorów. JeĈli zapomni-
my uĔyè säowa 

new

 przy wywoäywaniu konstruktora, 

this

 nie bödzie powiñzane z nowym

                                                       

1

Kod  ten  w  celu  poprawnego  nadpisania  metody 

get_name

  wymaga  uĔycia  implementacji  metody 

method

podanej na koþcu rozdziaäu 1, a nie jej modyfikacji warunkowej pokazanej na koþcu podrozdziaäu „Rozsze-
rzanie typów” w rozdziale 4. — przyp. täum.

background image

56

_

Rozdzia

ĥ 5. Dziedziczenie

obiektem, lecz z obiektem globalnym. Zamiast wiöc dodaè wäaĈciwoĈci do nowego obiektu, za-
Ĉmiecimy nimi globalnñ przestrzeþ nazw. To bardzo niebezpieczna moĔliwoĈè, tym bardziej,
Ĕe nie otrzymamy Ĕadnego ostrzeĔenia ani podczas kompilacji, ani podczas wykonania kodu.

Jest to powaĔny bäñd projektowy jözyka. Aby sobie jakoĈ z nim radziè, istnieje konwencja na-
kazujñca  nadawanie  wszystkim  funkcjom  peäniñcym  rolö  konstruktorów  (i  Ĕadnym  innym)
nazw zaczynajñcych siö od wielkich liter. To pozwala przynajmniej mieè nadziejö, Ĕe analiza
kodu pozwoli wyäapaè wszystkie brakujñce zastosowania 

new

. Lepszñ alternatywñ jest jednak

nie uĔywanie w ogóle säowa 

new

.

Pseudoklasyczna forma dziedziczenia  pozwala  poczuè  siö  wygodniej  programistom  niezna-
jñcym dobrze JavaScriptu, ale równoczeĈnie ukrywa ona prawdziwñ naturö tego jözyka. No-
tacja inspirowana podejĈciem klasycznym moĔe zachöcaè do tworzenia przesadnie gäöbokich
i  skomplikowanych  hierarchii.  WiökszoĈè  takich  skomplikowanych  hierarchii  klas  spowodo-
wana jest ograniczeniami statycznej kontroli typów. JavaScript nie ma takich ograniczeþ. W jö-
zykach klasycznych dziedziczenie klas jest jedynñ formñ wielokrotnego wykorzystania kodu.
JavaScript ma duĔo szersze moĔliwoĈci.

Okre

ļlenia obiektów

Czasami  zdarza  siö,  Ĕe  konstruktor  pobiera  bardzo  duĔñ  liczbö  parametrów.  MoĔe  to  byè
käopotliwe, bo na przykäad utrudnia zapamiötanie kolejnoĈè argumentów. W takich wypad-
kach  lepszym  rozwiñzaniem  moĔe  byè  napisanie  konstruktora,  który  pobiera  pojedyncze
okreĈlenie obiektu (ang. object specifier). OkreĈlenie takie zawiera specyfikacjö obiektu, który
ma byè skonstruowany. Tak wiöc, zamiast takiego wywoäania funkcji:

var myObject = maker(f, l, m, c, s);

moĔemy napisaè:

var myObject = maker({
   first: f,
   last: l,
   state: s,
   city: c
});

Argumenty  mogñ  byè  teraz  wymienione  w  dowolnej  kolejnoĈci,  mogñ  byè  pominiöte,  jeĈli
konstruktor jest w stanie przypisaè im wartoĈci domyĈlne, a caäy kod jest äatwiejszy w czytaniu.

PodejĈcie to moĔe mieè dodatkowe zalety przy korzystaniu z formatu JSON (patrz dodatek E).
Tekst  formatu  JSON  jest  w  stanie  opisaè  jedynie  dane,  ale  czasami  dane  reprezentujñ  jakiĈ
obiekt i wygodnie byäoby powiñzaè dane z jego metodami. Okazuje siö to banalne, gdy kon-
struktor  pobiera  okreĈlenie  obiektu,  poniewaĔ  moĔemy  po  prostu  przesäaè  obiekt  JSON  do
konstruktora, który z kolei zwróci w peäni utworzony obiekt.

Dziedziczenie prototypowe

W wypadku podejĈcia czysto prototypowego, nie uĔywamy w ogóle klas. Skupiamy siö wy-
äñcznie na obiektach. Dziedziczenie prototypowe jest koncepcyjnie prostsze od klasycznego:
nowy obiekt moĔe dziedziczyè wäaĈciwoĈci starego obiektu. Jest to moĔe mniej powszechne
podejĈcie, ale jest za to bardzo äatwe do zrozumienia. Punktem wyjĈcia jest utworzenie jakiegoĈ

background image

Dziedziczenie prototypowe

_

57

poĔytecznego  obiektu.  W  nastöpnym  kroku  moĔemy  utworzyè  wiele  obiektów  podobnych
do  niego.  Proces  klasyfikacji  polegajñcy  na  rozbiciu  aplikacji  na  zbiór  zagnieĔdĔonych  klas
abstrakcyjnych moĔe byè caäkowicie pominiöty.

Zacznijmy wiöc od utworzenia takiego poĔytecznego obiektu, uĔywajñc literaäu obiektowego:

var myMammal = {
   name : "Mój ssak",
   get_name : function () {
      return this.name;
   },
   says : function () {
      return this.saying || '';
   }
};

Majñc  obiekt  taki  jak  ten,  moĔemy  utworzyè  wiöcej  instancji  korzystajñc  z  funkcji 

Ob-

ject.beget

 z rozdziaäu 3. Nastöpnie moĔemy je dostosowaè do naszych potrzeb:

var myCat = Object.beget(myMammal);
myCat.name = 'Kicia';
myCat.saying = 'miau';
myCat.purr = function (n) {
    var i, s = '';
    for (i = 0; i < n; i += 1) {
       if (s) {
          s += '-';
       }
       s += 'r';
    }
    return s;
};
myCat.get_name = function () {
   return this.says() + ' ' + this.name + ' ' + this.says();
};

Jest to dziedziczenie róĔnicowe. Zdefiniowanie nowego obiektu polega na okreĈleniu róĔnic
miödzy nim a obiektem, z którego zostaä utworzony.

Czasami  wygodnie  jest,  gdy  struktura  danych  dziedziczy  z  innej  struktury.  Oto  przykäad:
przypuĈèmy,  Ĕe  analizujemy  kod  jözyka  takiego  jak  Java  lub  T

E

X,  w  którym  para  nawiasów

oznacza zasiög. Zmienne zdefiniowane w ramach zasiögu nie sñ widoczne poza nim. W pewnym
sensie  wewnötrzny  zasiög  dziedziczy  po  zewnötrznym.  Obiekty  JavaScriptu  dobrze  siö  na-
dajñ do reprezentacji takiej zaleĔnoĈci. Funkcja 

block

 wywoäywana jest, kiedy napotkany zo-

staje  lewy  nawias  klamrowy.  Funkcja 

parse

  bödzie  pobieraè  symbole  z  zasiögu  oraz  doda-

waè nowo napotkane:

var block = function () {

   // Zapami

Ċtujemy dotychczasowy zasiĊg. Tworzymy nowy zasiĊg,

   // który b

Ċdzie zawieraá wszystko to, co dotychczasowy.

   var oldScope = scope;
   scope = Object.beget(scope);

   // Przechodz

ąc nad lewym nawiasem klamrowym wchodzimy do nowego zasiĊgu.

   advance('{');

   // Analizujemy tekst, u

Īywając nowego zasiĊgu.

   parse(scope);

background image

58

_

Rozdzia

ĥ 5. Dziedziczenie

   // Przechodz

ąc nad prawym nawiasem klamrowym wychodzimy z zasiĊgu

   // i przywracamy stary zasi

Ċg.

   advance('}');
   scope = oldScope;
};

Dziedziczenie funkcyjne

Przedstawione dotñd wzorce dziedziczenia majñ pewien säaby punkt: brak prywatnoĈci. Wszyst-
kie wäaĈciwoĈci obiektów sñ widoczne. Brakuje zmiennych prywatnych oraz prywatnych me-
tod.  Czasami  nie  ma  to  wiökszego  znaczenia,  ale  czasami  moĔe  mieè  znaczenie  ogromne.
Zniechöceni  tym  faktem  niektórzy  niedoinformowani  programiĈci  ukuli  zasadö  uĔywania
zmiennych niby-prywatnych. Majñc wäaĈciwoĈè, którñ chcieli uczyniè prywatnñ, nadawali jej
dziwnie wyglñdajñcñ nazwö, majñc nadziejö, Ĕe inni uĔytkownicy ich kodu bödñ udawaè, Ĕe
nie widzñ nazw dziwnie wyglñdajñcych. Na szczöĈcie istnieje duĔo lepsza alternatywa oparta
o omawiany wczeĈniej wzorzec moduäu.

Zaczynamy od napisania  funkcji, która  bödzie wytwarzaè  obiekty. Dajemy jej nazwö zaczy-
najñcñ  siö  od  maäej  litery,  poniewaĔ  nie  bödzie  ona  wymagaè  uĔycia  säowa 

new

.  Dziaäanie

funkcji skäada siö z czterech kroków:

Funkcja  tworzy  nowy  obiekt.  Jest  na  to  wiele  sposobów:  uĔycie  literaäu  obiektowego,
wywoäanie funkcji konstruktora ze säowem 

new

, uĔycie metody 

Object.beget

 do utwo-

rzenia  nowego  obiektu  na  podstawie  istniejñcego,  wreszcie  wywoäanie  dowolnej  innej
funkcji zwracajñcej obiekt.

Opcjonalnie funkcja deklaruje zmienne i metody prywatne. Sñ to zwykäe  zmienne (

var

)

funkcji.

Nastöpnie  funkcja  rozszerza  nowo  utworzony  obiekt  o  metody.  Metody  te  majñ  uprzy-
wilejowany dostöp do parametrów i zmiennych zdefiniowanych w poprzednim kroku.

Na koniec nowy obiekt jest zwracany.

Oto szablon pseudokodu do tworzenia konstruktora funkcyjnego (pogrubiony tekst dodano
dla podkreĈlenia niektórych fragmentów):

var constructor = function (spec, my) {
   vat that, inne prywatne zmienne instancyjne;
   my = my || {};

   // tu dodajemy do obiektu my zmienne i funkcje wspóádzielone

   that = nowy obiekt;

   // tu dodajemy do obiektu that metody uprzywilejowane

   return that;
}

Obiekt 

spec

 zawiera wszystkie informacje niezbödne konstruktorowi do utworzenia  instancji.

ZawartoĈè obiektu 

spec

 moĔe byè skopiowana do zmiennych prywatnych lub przetworzona

przez inne funkcje, bñdĒ teĔ metody mogñ pobieraè informacje z tego obiektu w razie potrzeby.
(MoĔna teĔ to uproĈciè zastöpujñc 

spec

 pojedynczñ wartoĈciñ. Jest to wygodne, gdy tworzony

obiekt nie wymaga peänego obiektu 

spec

.)

background image

Dziedziczenie funkcyjne

_

59

Obiekt 

my

 säuĔy przechowywaniu chronionych danych, które mogñ byè wspóädzielone z kon-

struktorami w ramach äaþcucha dziedziczenia. UĔycie tego obiektu jest opcjonalne. JeĈli nie jest
on przekazany do funkcji, tworzony jest pusty.

Nastöpnie deklarujemy prywatne  zmienne instancyjne  oraz  prywatne metody  obiektu. Odby-
wa siö to poprzez zwykäe zadeklarowanie zmiennych. Zmienne i funkcje wewnötrzne utwo-
rzone wewnñtrz konstruktora stajñ siö zmiennymi i funkcjami prywatnymi instancji. Funkcje
wewnötrzne majñ dostöp do 

spec

my

that

 i innych zmiennych prywatnych.

Nastöpnie dodajemy wspóädzielone dane chronione do obiektu 

my

. Wykonuje siö to poprzez

przypisanie:

my.member = value;

Teraz  tworzymy  nowy  obiekt  i  przypisujemy  go  do 

that

.  Jest  wiele  sposobów  utworzenia

obiektu. MoĔemy uĔyè literaäu obiektowego. MoĔemy wywoäaè pseudoklasyczny konstruktor,
uĔywajñc  operatora 

new

.  MoĔemy  wywoäaè  metodö 

Object.beget

  na  prototypie  obiektu.

Wreszcie moĔemy skorzystaè z dowolnego innego konstruktora funkcyjnego, przekazujñc mu
obiekt 

spec

 (najczöĈciej ten sam, który zostaä przekazany do tego konstruktora) oraz obiekt 

my

.

Obiekt 

my

  pozwala  na  wspóädzielenie  informacji  z  innymi  konstruktorami.  Inne  konstruktory

mogñ równieĔ umieszczaè swoje wäasne chronione dane w tym obiekcie, w celu wspóädzie-
lenia ich z naszych konstruktorem.

Nastöpnie rozszerzamy 

that

, dodajñc metody uprzywilejowane stanowiñce interfejs obiektu.

MoĔemy przypisywaè nowe funkcje bezpoĈrednio do 

that

. Lub teĔ, w bardziej bezpieczny spo-

sób, najpierw zdefiniowaè funkcje jako metody prywatne, a nastöpnie przypisaè je do 

that

:

var methodical = function () {
   ...
};
that.methodical = methodical;

Zaletñ definiowania metod w dwóch etapach jest to, Ĕe jeĈli inne metody bödñ chciaäy wywo-
äywaè 

methodical

, bödñ mogäy zrobiè to przez wywoäanie 

methodical()

 zamiast 

that.metho

´dical()

.  JeĈli  instancja  zostanie  uszkodzona  lub  zmanipulowana,  tak  Ĕe  metoda 

that.

´methodical

  zostanie  zastñpiona  innñ,  wówczas  funkcje  wywoäujñce  funkcjö 

methodical

bödñ nadal dziaäaè poprawnie, poniewaĔ funkcja prywatna 

methodical

 pozostanie nienaru-

szona mimo modyfikacji obiektu.

Na koniec zwracamy 

that

.

Zastosujmy ten wzorzec do naszego przykäadu z ssakami. Nie potrzebujemy tutaj zmiennej 

my

,

wiöc po prostu jñ opuĈcimy, ale za to przyda nam siö obiekt 

spec

.

WäaĈciwoĈci 

name

 i 

saying

 sñ teraz caäkowicie prywatne. Dostöp do nich jest moĔliwy tylko

dziöki uprzywilejowanym metodom 

get_name

 i 

says

:

var mammal = function (spec) {
   var that = {};

   that.get_name = function () {
      return spec.name;
   };

   that.says = function () {
      return spec.saying || '';
   };

background image

Czytaj dalej...

60

_

Rozdzia

ĥ 5. Dziedziczenie

   return that;
};

var myMammal = mammal({name: 'Mój ssak'});

W podejĈciu pseudoklasycznym konstruktor 

Cat

 musiaä duplikowaè pracö wykonywanñ przez

konstruktor 

Mammal

. We wzorcu funkcyjnym nie jest to konieczne, poniewaĔ konstruktor 

Cat

wywoäa konstruktor 

Mammal

, który sam wykona swoje zadanie. Konstruktor 

Cat

 zajmuje siö

tylko róĔnicami miödzy nimi:

var cat = function (spec) {
   spec.saying = spec.saying || 'miau';
   var that = mammal(spec);
   that.purr = function (n) {
      var i, s = '';
      for (i = 0; i < n; i += 1) {
         if (s) {
            s += '-';
         }
         s += 'r';
      }
      return s;
   };
   that.get_name = function () {
      return that.says() + ' ' + spec.name + ' ' + that.says();
   };
return that;
};

var myCat = cat({name: 'Kicia'});

Wzorzec funkcyjny umoĔliwia nam równieĔ wywoäywanie metod z obiektów nadrzödnych.
Napiszmy metodö 

superior

, która pobieraè bödzie nazwö metody i zwracaè funkcjö wywo-

äujñcñ tö metodö. Funkcja ta bödzie wywoäywaè oryginalnñ metodö, nawet gdy wäaĈciwoĈè
zostaäa zmieniona przez obiekt potomny:

Object.method('superior', function (name) {
   var that = this,
       method = that[name];
   return function () {
      return method.apply(that, arguments);
   };
});

Wypróbujmy jñ na obiekcie 

coolcat

, który jest podobny do obiektu 

cat

, ale ma nieco ciekaw-

szñ metodö 

get_name

, która wywoäuje nieprzesäoniötñ wersjö metody z obiektu nadrzödnego.

Wymaga to tylko niewielkich przygotowaþ. Zadeklarujemy zmiennñ 

super_get_name

 i przypi-

szemy jej wynik wywoäania metody 

superior

:

var coolcat = function (spec) {
   var that = cat(spec),
       super_get_name = that.superior('get_name');
   that.get_name = function (n) {
      return 'Teraz ' + super_get_name() + ' w nowej, lepszej wersji';
   };
   return that;
};

var myCoolCat = coolcat({name: 'Kocur'});
var name = myCoolCat.get_name();  // 'Teraz miau Kocur miau w nowej, lepszej wersji'