Tytuł oryginału: Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript
Tłumaczenie: Tomasz Walczak
ISBN: 978-83-283-1418-4
Authorized translation from the English language edition, entitled: EFFECTIVE JAVASCRIPT: 68
SPECIFIC WAYS TO HARNESS THE POWER OF JAVASCRIPT; ISBN 0321812182; by David Herman;
published by Pearson Education, Inc, publishing as Addison Wesley.
Copyright © 2013 by Pearson Education, Inc.
All rights reserved. No part of this book may by 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 Pearson Education, Inc.
Polish language edition published by HELION S.A. Copyright © 2015.
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)
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/efprjs.zip
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/efprjs
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
Spis treĤci
Przedmowa .................................................................................... 11
Wprowadzenie ................................................................................ 13
Podzičkowania ............................................................................... 15
O autorze ....................................................................................... 17
Rozdziaã 1. Przyzwyczajanie sič do JavaScriptu ............................. 19
Sposób 1. Ustal, której wersji JavaScriptu uİywasz...................................... 19
Sposób 2. Liczby zmiennoprzecinkowe w JavaScripcie.................................. 24
Sposób 3. Uwaİaj na niejawnĈ konwersjč typu............................................. 27
Sposób 4. Stosuj typy proste zamiast nakãadek obiektowych ........................ 32
Sposób 5. Unikaj stosowania operatora == dla wartoĤci o róİnych typach .... 34
Sposób 6. Ograniczenia mechanizmu automatycznego
dodawania Ĥredników .................................................................. 37
Sposób 7. Traktuj ãaęcuchy znaków jak sekwencje
16-bitowych jednostek kodowych................................................. 43
Rozdziaã 2. Zasičg zmiennych ......................................................... 47
Sposób 8. Minimalizuj liczbč obiektów globalnych....................................... 47
Sposób 9. Zawsze deklaruj zmienne lokalne ................................................ 50
Sposób 10. Unikaj sãowa kluczowego with.................................................... 51
Sposób 11. Poznaj domkničcia ..................................................................... 54
Sposób 12. Niejawne przenoszenie deklaracji zmiennych
na poczĈtek bloku (czyli hoisting)............................................... 57
Sposób 13. Stosuj wyraİenia IIFE do tworzenia zasičgu lokalnego ................ 59
8
Spis treĤci
Sposób 14. Uwaİaj na nieprzenoĤne okreĤlanie zasičgu
nazwanych wyraİeę funkcyjnych............................................... 62
Sposób 15. Uwaİaj na nieprzenoĤne okreĤlanie zasičgu lokalnych
deklaracji funkcji w bloku ......................................................... 65
Sposób 16. Unikaj tworzenia zmiennych lokalnych
za pomocĈ funkcji eval .............................................................. 67
Sposób 17. Przedkãadaj poĤrednie wywoãania eval
nad bezpoĤrednie wywoãania tej funkcji ..................................... 68
Rozdziaã 3. Korzystanie z funkcji..................................................... 71
Sposób 18. Róİnice mičdzy wywoãaniami funkcji, metod i konstruktorów..... 71
Sposób 19. Funkcje wyİszego poziomu ........................................................ 74
Sposób 20. Stosuj instrukcjč call do wywoãywania metod
dla niestandardowego odbiorcy.................................................. 77
Sposób 21. Stosuj instrukcjč apply do wywoãywania funkcji
o róİnej liczbie argumentów....................................................... 79
Sposób 22. Stosuj sãowo kluczowe arguments do tworzenia funkcji
wariadycznych........................................................................... 81
Sposób 23. Nigdy nie modyfikuj obiektu arguments ..................................... 82
Sposób 24. Uİywaj zmiennych do zapisywania referencji
do obiektu arguments................................................................ 84
Sposób 25. Uİywaj instrukcji bind do pobierania metod o staãym odbiorcy... 85
Sposób 26. Uİywaj metody bind do wiĈzania funkcji
z podzbiorem argumentów (technika currying).............................. 87
Sposób 27. Wybieraj domkničcia zamiast ãaęcuchów znaków
do hermetyzowania kodu ........................................................... 88
Sposób 28. Unikaj stosowania metody toString funkcji ................................ 90
Sposób 29. Unikaj niestandardowych wãaĤciwoĤci przeznaczonych
do inspekcji stosu...................................................................... 92
Rozdziaã 4. Obiekty i prototypy ....................................................... 95
Sposób 30. Róİnice mičdzy instrukcjami prototype,
getPrototypeOf i __proto__.......................................................... 95
Sposób 31. Stosuj instrukcjč Object.getPrototypeOf zamiast __proto__ ......... 99
Sposób 32. Nigdy nie modyfikuj wãaĤciwoĤci __proto__ ............................... 100
Sposób 33. Uniezaleİnianie konstruktorów od instrukcji new .................... 101
Sposób 34. Umieszczaj metody w prototypach............................................ 103
Sposób 35. Stosuj domkničcia do przechowywania prywatnych danych ..... 105
Sposób 36. Stan egzemplarzy przechowuj tylko w nich samych .................. 107
Sposób 37. Zwracaj uwagč na niejawne wiĈzanie obiektu this .................... 109
Spis treĤci
9
Sposób 38. Wywoãywanie konstruktorów klasy bazowej
w konstruktorach klas pochodnych ......................................... 111
Sposób 39. Nigdy nie wykorzystuj ponownie nazw wãaĤciwoĤci
z klasy bazowej........................................................................ 115
Sposób 40. Unikaj dziedziczenia po klasach standardowych....................... 117
Sposób 41. Traktuj prototypy jak szczegóã implementacji ........................... 119
Sposób 42. Unikaj nieprzemyĤlanego stosowania techniki
monkey patching ..................................................................... 120
Rozdziaã 5. Tablice i sãowniki .........................................................123
Sposób 43. Budowanie prostych sãowników
na podstawie egzemplarzy typu Object..................................... 123
Sposób 44. Stosuj prototypy null, aby uniknĈþ zaĤmiecania
przez prototypy........................................................................ 126
Sposób 45. Uİywaj metody hasOwnProperty do zabezpieczania sič
przed zaĤmiecaniem przez prototypy
Sposób 46. Stosuj tablice zamiast sãowników
przy tworzeniu kolekcji uporzĈdkowanych ............................... 132
Sposób 47. Nigdy nie dodawaj enumerowanych wãaĤciwoĤci
do prototypu Object.prototype ................................................. 134
Sposób 48. Unikaj modyfikowania obiektu w trakcie enumeracji ................ 136
Sposób 49. Stosuj pčtlč for zamiast pčtli for…in
przy przechodzeniu po tablicy .................................................. 140
Sposób 50. Zamiast pčtli stosuj metody do obsãugi iteracji ......................... 142
Sposób 51. Wykorzystaj uniwersalne metody klasy Array
w obiektach podobnych do tablic ............................................. 146
Sposób 52. Przedkãadaj literaãy tablicowe nad konstruktor klasy Array ...... 148
Rozdziaã 6. Projekty bibliotek i interfejsów API ..............................151
Sposób 53. Przestrzegaj spójnych konwencji .............................................. 151
Sposób 54. Traktuj wartoĤþ undefined jak brak wartoĤci............................ 153
Sposób 55. Stosuj obiekty z opcjami do przekazywania argumentów
za pomocĈ sãów kluczowych..................................................... 157
Sposób 56. Unikaj niepotrzebnego przechowywania stanu ........................... 161
Sposób 57. OkreĤlaj typy na podstawie struktury,
aby tworzyþ elastyczne interfejsy.............................................. 164
Sposób 58. Róİnice mičdzy tablicami a obiektami podobnymi do tablic ...... 167
Sposób 59. Unikaj nadmiernej koercji ........................................................ 171
Sposób 60. Obsãuga ãaęcuchów metod ....................................................... 174
10
Spis treĤci
Rozdziaã 7. WspóãbieİnoĤþ ............................................................. 179
Sposób 61. Nie blokuj kolejki zdarzeę operacjami wejĤcia-wyjĤcia .............. 180
Sposób 62. Stosuj zagnieİdİone lub nazwane wywoãania zwrotne
do tworzenia sekwencji asynchronicznych wywoãaę ................. 183
Sposób 63. Pamičtaj o ignorowanych bãčdach ............................................ 187
Sposób 64. Stosuj rekurencjč do tworzenia asynchronicznych pčtli............ 190
Sposób 65. Nie blokuj kolejki zdarzeę obliczeniami .................................... 193
Sposób 66. Wykorzystaj licznik do wykonywania wspóãbieİnych operacji ... 197
Sposób 67. Nigdy nie uruchamiaj synchronicznie asynchronicznych
wywoãaę zwrotnych ................................................................. 201
Sposób 68. Stosuj obietnice, aby zwičkszyþ
przejrzystoĤþ asynchronicznego kodu ...................................... 203
Skorowidz .................................................................................... 207
Korzystanie z funkcji
Funkcje to „woãy robocze” JavaScriptu. SĈ dla programistów jednoczeĤnie
podstawowĈ abstrakcjĈ i mechanizmem implementacyjnym. Funkcje odgry-
wajĈ tu role, które w innych jčzykach sĈ przypisane do wielu róİnych elemen-
tów: procedur, metod, konstruktorów, a nawet klas i moduãów. Gdy zapoznasz
sič z subtelnymi aspektami funkcji, opanujesz istotnĈ czčĤþ JavaScriptu.
Pamičtaj jednak o tym, İe nauka efektywnego posãugiwania sič funkcjami
w róİnych kontekstach wymaga czasu.
Sposób 18. Róİnice mičdzy wywoãaniami funkcji, metod
i konstruktorów
JeĤli programowanie obiektowe nie jest Ci obce, prawdopodobnie traktujesz
funkcje, metody i konstruktory klas jako trzy odrčbne elementy. W Java-
Scripcie odpowiadajĈ im trzy róİne sposoby korzystania z jednej konstrukcji
— z funkcji.
Najprostszy sposób to wywoãanie funkcji:
function hello(username) {
return "Witaj, " + username;
}
hello("Keyser Söze"); // "Witaj, Keyser Söze"
Ten kod dziaãa w standardowy sposób — wywoãuje funkcjč
hello
i wiĈİe pa-
rametr
name
z podanym argumentem.
Metody w JavaScripcie to naleİĈce do obiektów wãaĤciwoĤci, które sĈ funk-
cjami:
var obj = {
hello: function() {
return "Witaj, " + this.username;
},
72
Rozdziaã 3. Korzystanie z funkcji
username: "Hans Gruber"
};
obj.hello(); // "Witaj, Hans Gruber"
Zauwaİ, İe w metodzie
hello
uİywane jest sãowo
this
, aby uzyskaþ dostčp
do obiektu
obj
. Moİe sič wydawaþ, İe
this
zostaje zwiĈzane z
obj
, poniewaİ
metodč
hello
zdefiniowano wãaĤnie w obiekcie
obj
. Jednak moİna skopiowaþ
referencjč do tej samej funkcji w innym obiekcie i uzyskaþ odmienny wynik:
var obj2 = {
hello: obj.hello,
username: "Boo Radley"
};
obj2.hello(); // "Witaj, Boo Radley"
W wywoãaniu metody samo wywoãanie okreĤla, z czym zwiĈzane jest sãowo
this
(wyznacza ono odbiorcč wywoãania). Wyraİenie
obj.hello()
powoduje
wyszukanie wãaĤciwoĤci
hello
obiektu
obj
i wywoãanie jej dla odbiorcy
obj
.
Wyraİenie
obj2.hello()
prowadzi do wyszukiwania wãaĤciwoĤci
hello
obiektu
obj2
; tĈ wãaĤciwoĤciĈ jest ta sama funkcja co w wywoãaniu
obj.hello
, tu jednak
jest ona wywoãywana dla odbiorcy
obj2
. Wywoãanie metody obiektu stan-
dardowo prowadzi do wyszukania metody i uİycia danego obiektu jako od-
biorcy tej metody.
Poniewaİ metody to funkcje wywoãane dla okreĤlonego obiektu, moİna swo-
bodnie wywoãywaþ zwykãe funkcje z wykorzystaniem sãowa kluczowego
this
:
function hello() {
return "Witaj, " + this.username;
}
Takie rozwiĈzanie moİe okazaþ sič przydatne, jeĤli chcesz wstčpnie zdefi-
niowaþ funkcjč, która bčdzie uİywana w wielu obiektach:
var obj1 = {
hello: hello,
username: "Gordon Gekko"
};
obj1.hello(); // "Witaj, Gordon Gekko"
var obj2 = {
hello: hello,
username: "Biff Tannen"
};
obj2.hello(); // "Witaj, Biff Tannen"
Jednak funkcja wykorzystujĈca sãowo kluczowe
this
nie jest przydatna, jeĤli
chcesz jĈ wywoãywaþ jak zwykãĈ funkcjč, a nie jak metodč:
hello(); // "Witaj, undefined"
Nie pomaga to, İe w zwykãych wywoãaniach funkcji (nie w metodach) odbior-
cĈ jest obiekt globalny, który tu nie ma wãaĤciwoĤci o nazwie
name
i zwraca
wartoĤþ
undefined
. Wywoãanie metody jako funkcji rzadko jest przydatne, jeĤli
Sposób 18. Róİnice mičdzy wywoãaniami funkcji, metod i konstruktorów
73
dana metoda wykorzystuje sãowo kluczowe
this
. PrzyczynĈ jest to, İe trudno
oczekiwaþ, iİ obiekt globalny bčdzie miaã te same wãaĤciwoĤci co obiekt, dla
którego napisano danĈ metodč. Uİywanie w tym kontekĤcie obiektu global-
nego jest na tyle problematycznym rozwiĈzaniem domyĤlnym, İe w trybie
strict w standardzie ES5
this
jest domyĤlnie wiĈzane z wartoĤciĈ
undefined
:
function hello() {
"use strict";
return "Witaj, " + this.username;
}
hello(); // Błąd: nie można znaleźć właściwości "username" obiektu undefined
To pomaga wykryþ przypadkowe wykorzystanie metod jako zwykãych funkcji.
Kod szybko przestanie wtedy dziaãaþ, poniewaİ próba dostčpu do wãaĤciwoĤci
obiektu
undefined
spowoduje natychmiastowe zgãoszenie bãčdu.
Trzecim sposobem uİywania funkcji jest ich wywoãywanie jako konstrukto-
rów. Konstruktory, podobnie jak metody i zwykãe funkcje, definiuje sič za
pomocĈ sãowa kluczowego
function
:
function User(name, passwordHash) {
this.name = name;
this.passwordHash = passwordHash;
}
JeĤli wywoãasz funkcjč
User
z operatorem
new
, zostanie ona potraktowana jak
konstruktor:
var u = new User("sfalken",
"0ef33ae791068ec64b502d6cb0191387");
u.name; // "sfalken"
Wywoãanie konstruktora, w odróİnieniu od wywoãaę funkcji i metod, powo-
duje przekazanie nowego obiektu jako wartoĤci
this
i niejawne zwrócenie no-
wego obiektu jako wyniku. Gãównym zadaniem konstruktora jest inicjowanie
obiektów.
Co warto zapamičtaþ?
Q
W wywoãaniach metod naleİy podaþ obiekt (odbiorcč), w którym szukana
bčdzie wãaĤciwoĤþ w postaci tej metody.
Q
W wywoãaniach funkcji odbiorcĈ jest obiekt globalny (w trybie strict jest to
wartoĤþ
undefined
). Wywoãywanie metod jak zwykãych funkcji rzadko jest
przydatne.
Q
Konstruktory sĈ wywoãywane za pomocĈ sãowa kluczowego
new
, a ich od-
biorcĈ sĈ nowe obiekty.
74
Rozdziaã 3. Korzystanie z funkcji
Sposób 19. Funkcje wyİszego poziomu
Funkcje wyİszego poziomu byãy w przeszãoĤci znakiem rozpoznawczym
mistrzów programowania funkcyjnego. Te tajemnicze nazwy sugerujĈ, İe ma-
my tu do czynienia z zaawansowanĈ technikĈ programistycznĈ. Nic bardziej
mylnego. Wykorzystanie zwičzãej elegancji funkcji czčsto prowadzi do powsta-
nia prostszego i krótszego kodu. Przez lata w jčzykach skryptowych wprowa-
dzano techniki z tego obszaru. Dzički temu udaão sič przybliİyþ uİytkownikom
niektóre z najlepszych idiomów z dziedziny programowania funkcyjnego.
Funkcje wyİszego poziomu to po prostu funkcje, które przyjmujĈ inne funkcje
jako argumenty lub zwracajĈ inne funkcje jako wynik. Zwãaszcza przyjmo-
wanie argumentu w postaci funkcji (nazywanej czčsto funkcjĈ wywoãywanĈ
zwrotnie, poniewaİ jest wywoãywana przez funkcjč wyİszego poziomu) to
wyjĈtkowo przydatny i zwičzãy idiom, czčsto wykorzystywany w programach
w JavaScripcie.
Przyjrzyj sič standardowej metodzie
sort
tablic. Aby mogãa ona dziaãaþ dla
wszystkich moİliwych tablic, wymaga od programu wywoãujĈcego okreĤlenia,
jak naleİy porównywaþ dwa elementy tablicy:
function compareNumbers(x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
}
[3, 1, 4, 1, 5, 9].sort(compareNumbers); // [1, 1, 3, 4, 5, 9]
W bibliotece standardowej moİna byão zaİĈdaþ, aby program wywoãujĈcy
przekazywaã obiekt z metodĈ
compare
, jednak poniewaİ wymagana jest tylko
jedna metoda, bezpoĤrednie przyjmowanie funkcji to prostsze i bardziej zwič-
zãe rozwiĈzanie. Przedstawiony wczeĤniej przykãad moİna uproĤciþ jeszcze
bardziej, stosujĈc funkcjč anonimowĈ:
[3, 1, 4, 1, 5, 9].sort(function(x, y) {
if (x < y) {
return -1;
}
if (x > y) {
return 1;
}
return 0;
}); // [1, 1, 3, 4, 5, 9]
Opanowanie funkcji wyİszego poziomu czčsto pozwala uproĤciþ kod i wyeli-
minowaþ nudny, szablonowy kod. Dla wielu typowych operacji na tablicach
istniejĈ Ĥwietne abstrakcje wyİszego rzčdu, z którymi warto sič zapoznaþ.
Sposób 19. Funkcje wyİszego poziomu
75
Przyjrzyj sič prostemu zadaniu przeksztaãcania tablicy ãaęcuchów znaków.
Za pomocĈ pčtli moİna napisaþ nastčpujĈcy kod:
var names = ["Fred", "Wilma", "Pebbles"];
var upper = [];
for (var i = 0, n = names.length; i < n; i++) {
upper[i] = names[i].toUpperCase();
}
upper; // ["FRED", "WILMA", "PEBBLES"]
Dzički wygodnej metodzie
map
tablic (wprowadzonej w standardzie ES5)
moİna caãkowicie zrezygnowaþ z pčtli i zaimplementowaþ transformacjč ko-
lejnych elementów za pomocĈ funkcji lokalnej:
var names = ["Fred", "Wilma", "Pebbles"];
var upper = names.map(function(name) {
return name.toUpperCase();
});
upper; // ["FRED", "WILMA", "PEBBLES"]
Gdy przyzwyczaisz sič do korzystania z funkcji wyİszego poziomu, zaczniesz
dostrzegaþ okazje do ich samodzielnego pisania. DobrĈ oznakĈ wskazujĈcĈ,
İe taka funkcja moİe okazaþ sič przydatna, jest wystčpowanie powtarzajĈce-
go sič lub podobnego kodu. Zaãóİmy, İe jedna czčĤþ programu tworzy ãaę-
cuch znaków skãadajĈcy sič z liter alfabetu:
var aIndex = "a".charCodeAt(0); // 97
var alphabet = "";
for (var i = 0; i < 26; i++) {
alphabet += String.fromCharCode(aIndex + i);
}
alphabet; // "abcdefghijklmnopqrstuvwxyz"
Inna czčĤþ programu generuje ãaęcuch znaków obejmujĈcy cyfry:
var digits = "";
for (var i = 0; i < 10; i++) {
digits += i;
}
digits; // "0123456789"
W jeszcze innym fragmencie program tworzy ãaęcuch z losowych znaków:
var random = "";
for (var i = 0; i < 8; i++) {
random += String.fromCharCode(Math.floor(Math.random() * 26)
+ aIndex);
}
random; // "bdwvfrtp" (za każdym razem wynik jest inny)
Kaİdy fragment generuje inny ãaęcuch znaków, jednak logika dziaãania jest za
kaİdym razem podobna. Kaİda z pokazanych pčtli generuje ãaęcuch zna-
ków na podstawie scalania wyników obliczeę dajĈcych poszczególne znaki.
76
Rozdziaã 3. Korzystanie z funkcji
Moİna wyodrčbniþ wspólne aspekty i umieĤciþ je w jednej funkcji narzč-
dziowej:
function buildString(n, callback) {
var result = "";
for (var i = 0; i < n; i++) {
result += callback(i);
}
return result;
}
Zauwaİ, İe w funkcji
buildString
znajdujĈ sič wszystkie wspólne elementy
kaİdej pčtli, natomiast dla zmiennych aspektów stosowane sĈ parametry.
Liczbie iteracji pčtli odpowiada zmienna
n
, a do tworzenia kaİdego fragmentu
ãaęcucha sãuİy funkcja
callback
. Teraz moİna uproĤciþ kaİdy z trzech
przykãadów i zastosowaþ w nich funkcjč
buildString
:
var alphabet = buildString(26, function(i) {
return String.fromCharCode(aIndex + i);
});
alphabet; // "abcdefghijklmnopqrstuvwxyz"
var digits = buildString(10, function(i) { return i; });
digits; // "0123456789"
var random = buildString(8, function() {
return String.fromCharCode(Math.floor(Math.random() * 26)
+ aIndex);
});
random; // "ltvisfjr" (wynik za każdym razem jest inny)
Tworzenie abstrakcji wyİszego poziomu przynosi wiele korzyĤci. JeĤli w im-
plementacji wystčpujĈ skomplikowane fragmenty (trzeba na przykãad wãaĤci-
wie obsãuİyþ warunki graniczne dla pčtli), znajdujĈ sič one w funkcji wyİ-
szego poziomu. Dzički temu wystarczy naprawiþ bãčdy w logice raz, zamiast
szukaþ wszystkich wystĈpieę danego wzorca rozrzuconych po programie.
Takİe jeĤli stwierdzisz, İe trzeba zoptymalizowaþ wydajnoĤþ operacji, wystar-
czy to zrobiþ w jednym miejscu. Ponadto nadanie abstrakcji jednoznacznej
nazwy (takiej jak
buildString
, czyli „twórz ãaęcuch znaków”) jasno informuje
czytelników kodu o jego dziaãaniu. Dzički temu nie trzeba analizowaþ szcze-
góãów implementacji.
Stosowanie funkcji wyİszego poziomu po dostrzeİeniu, İe w kodzie powtarza sič
ten sam wzorzec, prowadzi do powstawania bardziej zwičzãego kodu, zwičk-
szenia produktywnoĤci i poprawy czytelnoĤci kodu. Zwracanie uwagi na po-
wtarzajĈce sič wzorce i przenoszenie ich do funkcji narzčdziowych wyİszego
poziomu to waİny nawyk, który warto sobie rozwinĈþ.
Sposób 20. Instrukcja call do wywoãywania metod dla niestandardowego odbiorcy
77
Co warto zapamičtaþ?
Q
Funkcje wyİszego poziomu charakteryzujĈ sič tym, İe przyjmujĈ inne
funkcje jako argumenty lub zwracajĈ funkcje jako wyniki.
Q
Zapoznaj sič z funkcjami wyİszego poziomu z istniejĈcych bibliotek.
Q
Naucz sič wykrywaþ powtarzajĈce sič wzorce, które moİna zastĈpiþ funk-
cjami wyİszego poziomu.
Sposób 20. Instrukcja call do wywoãywania metod dla niestandardowego odbiorcy
Sposób 20. Stosuj instrukcjč call do wywoãywania metod
dla niestandardowego odbiorcy
Odbiorca funkcji lub metody (czyli wartoĤþ wiĈzana ze specjalnym sãowem
kluczowym
this
) standardowo jest okreĤlany na podstawie skãadni wywoãa-
nia. Wywoãanie metody powoduje zwiĈzanie ze sãowem kluczowym
this
obiektu, w którym dana metoda jest wyszukiwana. Czasem jednak trzeba
wywoãaþ funkcjč dla niestandardowego odbiorcy, a nie jest ona jego wãaĤciwo-
ĤciĈ. Moİna oczywiĤcie dodaþ potrzebnĈ metodč jako nowĈ wãaĤciwoĤþ danego
obiektu:
obj.temporary = f; // Co się stanie, jeśli właściwość obj.temporary już istniała?
var result = obj.temporary(arg1, arg2, arg3);
delete obj.temporary; // Co się stanie, jeśli właściwość obj.temporary już istniała?
Jednak to podejĤcie jest niewygodne, a nawet niebezpieczne. Czčsto nie nale-
İy, a nawet nie da sič zmodyfikowaþ obiektu takiego jak
obj
w przykãadzie.
Niezaleİnie od nazwy wybranej dla wãaĤciwoĤci (tu jest to
temporary
) istnieje
ryzyko kolizji z istniejĈcĈ wãaĤciwoĤciĈ obiektu. Ponadto niektóre obiekty
sĈ zamroİone lub zamkničte, co uniemoİliwia dodawanie do nich nowych
wãaĤciwoĤci. Ponadto dodawanie dowolnych wãaĤciwoĤci do obiektów to zãa
praktyka — zwãaszcza gdy sĈ to obiekty utworzone przez innego programistč
(zobacz Sposób 42.).
Na szczčĤcie funkcje majĈ wbudowanĈ metodč
call
, umoİliwiajĈcĈ podanie
niestandardowego odbiorcy. Wywoãanie funkcji za pomocĈ metody
call
:
f.call(obj, arg1, arg2, arg3);
dziaãa podobnie jak wywoãanie bezpoĤrednie:
f(arg1, arg2, arg3);
Róİnica polega na tym, İe w metodzie
call
pierwszy argument to jawnie
wskazany obiekt odbiorcy.
Metoda
call
jest wygodna przy wywoãywaniu metod, które mogãy zostaþ usu-
ničte, zmodyfikowane lub zastĈpione. W sposobie 45. przedstawiony jest przy-
datny przykãad, ilustrujĈcy wywoãywanie metody
hasOwnProperty
dla dowolnych
78
Rozdziaã 3. Korzystanie z funkcji
obiektów (nawet dla sãownika). W sãowniku sprawdzenie wãaĤciwoĤci
hasOw-
nProperty
powoduje zwrócenie wartoĤci ze sãownika, zamiast wywoãania
odziedziczonej metody:
dict.hasOwnProperty = 1;
dict.hasOwnProperty("foo"); // Błąd: 1 nie jest funkcją
Za pomocĈ metody
call
moİna wywoãaþ metodč
hasOwnProperty
dla sãownika,
nawet jeĤli nie jest ona zapisana w samym obiekcie:
var hasOwnProperty = {}.hasOwnProperty;
dict.foo = 1;
delete dict.hasOwnProperty;
hasOwnProperty.call(dict, "foo"); // true
hasOwnProperty.call(dict, "hasOwnProperty"); // false
Metoda
call
jest przydatna takİe przy definiowaniu funkcji wyİszego poziomu.
Czčsto stosowany idiom dotyczĈcy funkcji wyİszego poziomu polega na przyj-
mowaniu opcjonalnych argumentów okreĤlajĈcych odbiorcč, dla którego
funkcja ma zostaþ wywoãana. Na przykãad obiekt reprezentujĈcy tablicč
z parami klucz – wartoĤþ moİe udostčpniaþ metodč
forEach
:
var table = {
entries: [],
addEntry: function(key, value) {
this.entries.push({ key: key, value: value });
},
forEach: function(f, thisArg) {
var entries = this.entries;
for (var i = 0, n = entries.length; i < n; i++) {
var entry = entries[i];
f.call(thisArg, entry.key, entry.value, i);
}
}
};
To umoİliwia uİytkownikom obiektu podanie potrzebnej metody jako wy-
woãywanej zwrotnie funkcji
f
z metody
table.forEach
i wskazanie odpowied-
niego odbiorcy. Dzički temu moİna na przykãad wygodnie skopiowaþ za-
wartoĤþ jednej tablicy do drugiej:
table1.forEach(table2.addEntry, table2);
Ten kod uİywa metody
addEntry
obiektu
table2
(moİna teİ pobraþ tč metodč
z obiektu
Table.prototype
lub
table1
), a metoda
forEach
wielokrotnie wywoãuje
metodč
addEntry
dla odbiorcy
table2
. Zauwaİ, İe choþ metoda
addEntry
oczekuje
tylko dwóch argumentów, metoda
forEach
wywoãuje jĈ z trzema argumentami:
kluczem, wartoĤciĈ i indeksem. Dodatkowy argument w postaci indeksu
nie powoduje problemów, poniewaİ metoda
addEntry
po prostu go pomija.
Sposób 21. Instrukcja apply do wywoãywania funkcji o róİnej liczbie argumentów
79
Co warto zapamičtaþ?
Q
Uİywaj metody
call
do wywoãywania funkcji dla niestandardowych od-
biorców.
Q
Stosuj metodč
call
do wywoãywania metod, które mogĈ nie istnieþ w danym
obiekcie.
Q
Uİywaj metody
call
do definiowania funkcji wyİszego poziomu, umoİli-
wiajĈcych klientom okreĤlanie odbiorcy wywoãywanych zwrotnie funkcji.
Sposób 21. Instrukcja apply do wywoãywania funkcji o róİnej liczbie argumentów
Sposób 21. Stosuj instrukcjč apply do wywoãywania funkcji
o róİnej liczbie argumentów
WyobraĮ sobie, İe dostčpna jest funkcja obliczajĈca ĤredniĈ z dowolnej
liczby wartoĤci:
average(1, 2, 3); // 2
average(1); // 1
average(3, 1, 4, 1, 5, 9, 2, 6, 5); // 4
average(2, 7, 1, 8, 2, 8, 1, 8); // 4.625
Funkcja
average
jest funkcjĈ wariadycznĈ (ang. variadic), inaczej funkcjĈ
o zmiennej arnoĤci (arnoĤþ funkcji to liczba oczekiwanych przez niĈ argu-
mentów). Oznacza to tyle, İe moİe przyjmowaþ dowolnĈ liczbč argumentów.
Wersja funkcji
average
majĈca staãĈ arnoĤþ prawdopodobnie pobieraãaby je-
den argument z tablicĈ wartoĤci:
averageOfArray([1, 2, 3]); // 2
averageOfArray([1]); // 1
averageOfArray([3, 1, 4, 1, 5, 9, 2, 6, 5]); // 4
averageOfArray([2, 7, 1, 8, 2, 8, 1, 8]); // 4.625
Wersja wariadyczna jest bardziej zwičzãa i (choþ to kwestia dyskusyjna) bar-
dziej elegancka. Funkcje wariadyczne majĈ wygodnĈ skãadnič — przynajm-
niej wtedy, gdy uİytkownik od razu wie, ile argumentów chce podaþ. Tak
dzieje sič w przedstawionych przykãadach. WyobraĮ sobie jednak, İe tablica
wartoĤci jest tworzona w nastčpujĈcy sposób:
var scores = getAllScores();
Jak uİyþ funkcji
average
do obliczenia Ĥredniej z tych wartoĤci?
average(/* ? */);
Na szczčĤcie funkcje majĈ wbudowanĈ metodč
apply
. Dziaãa ona podobnie jak
metoda
call
, ale jest zaprojektowana w okreĤlonym celu. Metoda
apply
przyj-
muje tablicč argumentów i wywoãuje danĈ funkcjč w taki sposób, jakby kaİdy
element tablicy byã odrčbnym argumentem wywoãania tej funkcji. Oprócz
80
Rozdziaã 3. Korzystanie z funkcji
tablicy argumentów metoda
apply
przyjmuje dodatkowo pierwszy argument,
okreĤlajĈcy obiekt
this
dla wywoãywanej funkcji. Poniewaİ funkcja
average
nie uİywa obiektu
this
, wystarczy podaþ wartoĤþ
null
:
var scores = getAllScores();
average.apply(null, scores);
JeĤli tablica
scores
bčdzie miaãa na przykãad trzy elementy, przedstawiony
kod zadziaãa tak samo jak poniİsza wersja:
average(scores[0], scores[1], scores[2]);
Metoda
apply
dziaãa takİe dla metod wariadycznych. Na przykãad obiekt
buffer
moİe mieþ wariadycznĈ metodč
append
, przeznaczonĈ do dodawania elemen-
tów do jego wewnčtrznego stanu (aby zrozumieþ implementacjč tej metody
append
, zapoznaj sič ze Sposobem 22.):
var buffer = {
state: [],
append: function() {
for (var i = 0, n = arguments.length; i < n; i++) {
this.state.push(arguments[i]);
}
}
};
Metodč
append
moİna wywoãaþ z dowolnĈ liczbĈ argumentów:
buffer.append("Witaj, ");
buffer.append(firstName, " ", lastName, "!");
buffer.append(newline);
Za pomocĈ argumentu metody
apply
okreĤlajĈcego obiekt
this
moİna teİ
wywoãaþ tč metodč z generowanĈ tablicĈ:
buffer.append.apply(buffer, getInputStrings());
Zwróþ uwagč na znaczenie argumentu
buffer
. JeĤli przekaİesz niewãaĤciwy
obiekt, metoda
append
spróbuje zmodyfikowaþ wãaĤciwoĤþ
state
nieodpowied-
niego obiektu.
Co warto zapamičtaþ?
Q
Stosuj metodč
apply
do wywoãywania funkcji wariadycznych z generowa-
nymi tablicami argumentów.
Q
Za pomocĈ pierwszego argumentu metody
apply
moİesz wskazaþ odbiorcč
metod wariadycznych.
Sposób 22. Stosuj sãowo kluczowe arguments do tworzenia funkcji wariadycznych
81
Sposób 22. Stosuj sãowo kluczowe arguments
do tworzenia funkcji wariadycznych
W sposobie 21. znajduje sič opis funkcji wariadycznej
average
. Potrafi ona zwró-
ciþ ĤredniĈ z dowolnej liczby argumentów. Jak samodzielnie zaimplemen-
towaþ funkcjč wariadycznĈ? Napisanie funkcji
averageOfArray
o staãej arnoĤci
jest stosunkowo ãatwe:
function averageOfArray(a) {
for (var i = 0, sum = 0, n = a.length; i < n; i++) {
sum += a[i];
}
return sum / n;
}
averageOfArray([2, 7, 1, 8, 2, 8, 1, 8]); // 4.625
W tej definicji funkcji
averageOfArray
uİywany jest jeden parametr formalny
— zmienna
a
z listy parametrów. Gdy uİytkownicy wywoãujĈ funkcjč
averageOf
´
Array
, podajĈ pojedynczy argument (nazywany tak w celu odróİnienia od
parametru formalnego). Jest nim tablica wartoĤci.
Wersja wariadyczna wyglĈda niemal tak samo, ale nie sĈ w niej jawnie zdefi-
niowane İadne parametry formalne. Zamiast tego wykorzystywany jest fakt,
İe JavaScript dodaje do kaİdej funkcji niejawnĈ zmiennĈ lokalnĈ o nazwie
arguments
. Obiekt
arguments
umoİliwia dostčp do argumentów w sposób przypo-
minajĈcy uİywanie tablicy. Kaİdy argument ma okreĤlony indeks, a wãaĤci-
woĤþ
length
okreĤla, ile argumentów zostaão podanych. To sprawia, İe w funkcji
average
o zmiennej arnoĤci moİna przejĤþ w pčtli po wszystkich elementach
z obiektu
arguments
:
function average() {
for (var i = 0, sum = 0, n = arguments.length;
i < n;
i++) {
sum += arguments[i];
}
return sum / n;
}
Funkcje wariadyczne dajĈ duİo swobody. W róİnych miejscach moİna je wy-
woãywaþ z wykorzystaniem odmiennej liczby argumentów. Jednak same
w sobie majĈ pewne ograniczenia, poniewaİ gdy uİytkownicy chcĈ je wywo-
ãaþ dla generowanej tablicy argumentów, muszĈ zastosowaþ opisanĈ w spo-
sobie 21. metodč
apply
. Gdy w celu uãatwienia pracy udostčpniasz funkcjč
o zmiennej arnoĤci, to zgodnie z ogólnĈ reguãĈ powinieneĤ teİ utworzyþ wersjč
o staãej arnoĤci, przyjmujĈcĈ jawnie podanĈ tablicč. Zwykle jest to ãatwe, po-
niewaİ przewaİnie moİna zaimplementowaþ funkcjč wariadycznĈ jako prostĈ
nakãadkč przekazujĈcĈ zadania do wersji o staãej arnoĤci:
82
Rozdziaã 3. Korzystanie z funkcji
function average() {
return averageOfArray(arguments);
}
Dzički temu uİytkownicy funkcji nie muszĈ uciekaþ sič do stosowania meto-
dy
apply
, która moİe zmniejszaþ czytelnoĤþ kodu i czčsto prowadzi do spadku
wydajnoĤci.
Co warto zapamičtaþ?
Q
Stosuj niejawny obiekt
arguments
do implementowania funkcji o zmiennej
arnoĤci.
Q
PomyĤl o udostčpnieniu obok funkcji wariadycznych dodatkowych wersji
o staãej arnoĤci, aby uİytkownicy nie musieli stosowaþ metody
apply
.
Sposób 23. Nigdy nie modyfikuj obiektu arguments
Obiekt
arguments
wprawdzie wyglĈda jak tablica, ale niestety nie zawsze dziaãa
w ten sposób. ProgramiĤci znajĈcy Perla i uniksowe skrypty powãoki sĈ
przyzwyczajeni do stosowania techniki przesuwania elementów w kierunku
poczĈtku tablicy argumentów. Tablice w JavaScripcie udostčpniajĈ metodč
shift
, która usuwa pierwszy element tablicy i przesuwa wszystkie kolejne
elementy o jednĈ pozycjč. Jednak obiekt
arguments
nie jest egzemplarzem
standardowego typu
Array
, dlatego nie moİna bezpoĤrednio wywoãaþ metody
arguments.shift()
.
Moİe sič wydawaþ, İe dzički metodzie
call
da sič pobraþ metodč
shift
tablic
i wywoãaþ jĈ dla obiektu
arguments
. Na pozór jest to sensowny sposób imple-
mentacji funkcji takiej jak
callMethod
, która przyjmuje obiekt i nazwč metody
oraz próbuje wywoãaþ wskazanĈ metodč tego obiektu dla wszystkich pozo-
staãych argumentów:
function callMethod(obj, method) {
var shift = [].shift;
shift.call(arguments);
shift.call(arguments);
return obj[method].apply(obj, arguments);
}
Jednak dziaãanie tej funkcji jest dalekie od oczekiwanego:
var obj = {
add: function(x, y) { return x + y; }
};
callMethod(obj, "add", 17, 25);
// Błąd: nie można wczytać właściwości "apply" obiektu undefined
Sposób 23. Nigdy nie modyfikuj obiektu arguments
83
PrzyczynĈ problemów z tym kodem jest to, İe obiekt
arguments
nie jest kopiĈ
argumentów funkcji. Wszystkie nazwane argumenty to aliasy odpowiednich
indeksów z obiektu
arguments
. Tak wičc
obj
to alias dla
arguments[0]
, a
method
to alias dla
arguments[1]
. Jest tak nawet po usuničciu elementów z obiektu
arguments
za pomocĈ wywoãania
shift
. To oznacza, İe choþ na pozór uİywane
jest wywoãanie
obj["add"]
, w rzeczywistoĤci wywoãanie to
17[25]
. Na tym etapie
zaczynajĈ sič kãopoty. Z powodu obowiĈzujĈcej w JavaScripcie automatycz-
nej konwersji typów wartoĤþ
17
jest przeksztaãcana w obiekt typu
Number
, po
czym pobierana jest jego (nieistniejĈca) wãaĤciwoĤþ
"25"
, dlatego zwrócona
zostaje wartoĤþ
undefined
. Potem nastčpuje nieudana próba pobrania wãa-
ĤciwoĤci
"apply"
obiektu
undefined
w celu wywoãania jej jako metody.
Wniosek z tego jest taki, İe relacja mičdzy obiektem
arguments
a nazwanymi pa-
rametrami funkcji ãatwo staje sič Įródãem problemów. Modyfikacja obiektu
arguments
grozi przeksztaãceniem nazwanych parametrów funkcji w bezsen-
sowne dane. W trybie strict ze standardu ES5 komplikacje sĈ jeszcze wičksze.
Parametry funkcji w tym trybie nie sĈ aliasami elementów obiektu
arguments
.
Aby zademonstrowaþ róİnicč, moİna napisaþ funkcjč aktualizujĈcĈ element
z obiektu
arguments
:
function strict(x) {
"use strict";
arguments[0] = "zmodyfikowany";
return x === arguments[0];
}
function nonstrict(x) {
arguments[0] = "zmodyfikowany";
return x === arguments[0];
}
strict("niezmodyfikowany"); // false
nonstrict("niezmodyfikowany"); // true
Dlatego duİo bezpieczniej jest nigdy nie modyfikowaþ obiektu
arguments
. Moİ-
na ãatwo uzyskaþ ten efekt, kopiujĈc najpierw elementy z tego obiektu do
zwykãej tablicy. Oto prosty idiom ilustrujĈcy takĈ modyfikacjč:
var args = [].slice.call(arguments);
Metoda
slice
tablic tworzy kopič tablicy, gdy zostanie wywoãana bez dodatko-
wych argumentów. Zwracany wynik to egzemplarz standardowego typu
Array
.
Ten egzemplarz nie jest aliasem İadnych obiektów i umoİliwia bezpoĤredni
dostčp do wszystkich metod typu
Array
.
Aby naprawiþ implementacjč metody
callMethod
, naleİy skopiowaþ zawartoĤþ
obiektu
arguments
. Poniewaİ potrzebne sĈ tylko elementy po
obj
i
method
, do
metody
slice
moİna przekazaþ poczĈtkowy indeks równy
2
:
function callMethod(obj, method) {
var args = [].slice.call(arguments, 2);
return obj[method].apply(obj, args);
}
84
Rozdziaã 3. Korzystanie z funkcji
Teraz metoda
callMethod
dziaãa zgodnie z oczekiwaniami:
var obj = {
add: function(x, y) { return x + y; }
};
callMethod(obj, "add", 17, 25); // 42
Co warto zapamičtaþ?
Q
Nigdy nie modyfikuj obiektu
arguments
.
Q
JeĤli chcesz zmodyfikowaþ zawartoĤþ obiektu
arguments
, najpierw skopiuj
jĈ do zwykãej tablicy za pomocĈ instrukcji
[].slice.call(arguments)
.
Sposób 24. Uİywaj zmiennych do zapisywania referencji
do obiektu arguments
Iterator to obiekt, który zapewnia sekwencyjny dostčp do kolekcji danych.
Typowy interfejs API udostčpnia metodč
next
, która zwraca nastčpnĈ wartoĤþ
z sekwencji. Zaãóİmy, İe chcesz napisaþ uãatwiajĈcĈ pracč funkcjč, która
przyjmuje dowolnĈ liczbč argumentów i tworzy iterator do poruszania sič po
tych wartoĤciach:
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // 1
it.next(); // 4
it.next(); // 1
Funkcja
values
musi przyjmowaþ dowolnĈ liczbč argumentów, dlatego obiekt
iteratora naleİy utworzyþ tak, aby przechodziã po elementach obiektu
arguments
:
function values() {
var i = 0, n = arguments.length;
return {
hasNext: function() {
return i < n;
},
next: function() {
if (i >= n) {
throw newError("Koniec iteracji");
}
return arguments[i++]; // Nieprawidłowe argumenty
}
};
}
Jednak ten kod jest nieprawidãowy. Staje sič to oczywiste przy próbie uİycia
iteratora:
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // undefined
Sposób 25. Instrukcja bind do pobierania metod o staãym odbiorcy
85
it.next(); // undefined
it.next(); // undefined
Problem wynika z tego, İe nowa zmienna
arguments
jest niejawnie wiĈzana
w ciele kaİdej funkcji. Obiekt, który jest tu potrzebny, jest zwiĈzany z funk-
cjĈ
values
. Ale metoda
next
iteratora zawiera wãasnĈ zmiennĈ
arguments
. Dlate-
go gdy zwracana jest wartoĤþ
arguments[i++]
, pobierany jest argument z wy-
woãania
it.next
, a nie jeden z argumentów funkcji
values
.
RozwiĈzanie tego problemu jest proste — wystarczy zwiĈzaþ nowĈ zmiennĈ
lokalnĈ w zasičgu potrzebnego obiektu
arguments
i zadbaþ o to, aby funkcje za-
gnieİdİone uİywaãy tylko tej jawnie nazwanej zmiennej:
function values() {
var i = 0, n = arguments.length, a = arguments;
return {
hasNext: function() {
return i < n;
},
next: function() {
if(i >= n) {
throw newError("Koniec iteracji");
}
return a[i++];
}
};
}
var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6);
it.next(); // 1
it.next(); // 4
it.next(); // 1
Co warto zapamičtaþ?
Q
Zwracaj uwagč na poziom zagnieİdİenia funkcji, gdy uİywasz obiektu
arguments
.
Q
ZwiĈİ z obiektem
arguments
referencjč o jawnie okreĤlonym zasičgu, aby
móc uİywaþ jej w funkcjach zagnieİdİonych.
Sposób 25. Instrukcja bind do pobierania metod o staãym odbiorcy
Sposób 25. Uİywaj instrukcji bind do pobierania metod
o staãym odbiorcy
Poniewaİ nie istnieje róİnica mičdzy metodĈ a wãaĤciwoĤciĈ, której wartoĤciĈ
jest funkcja, ãatwo moİna pobraþ metodč obiektu i przekazaþ jĈ bezpoĤrednio
jako wywoãanie zwrotne do funkcji wyİszego poziomu. Nie zapominaj jednak,
İe odbiorca pobranej funkcji nie jest na staãe ustawiony jako obiekt, z którego
tč funkcjč pobrano. WyobraĮ sobie prosty obiekt bufora ãaęcuchów znaków,
który zapisuje ãaęcuchy w tablicy, co umoİliwia ich póĮniejsze scalenie:
86
Rozdziaã 3. Korzystanie z funkcji
var buffer = {
entries: [],
add: function(s) {
this.entries.push(s);
},
concat: function() {
return this.entries.join("");
}
};
Wydaje sič, İe w celu skopiowania tablicy ãaęcuchów znaków do bufora moİ-
na pobraþ jego metodč
add
i wielokrotnie wywoãaþ jĈ dla kaİdego elementu
Įródãowej tablicy, uİywajĈc metody
forEach
ze standardu ES5:
var source = ["867", "-", "5309"];
source.forEach(buffer.add); // Błąd: elementy są niezdefiniowane
Jednak odbiorcĈ wywoãania
buffer.add
nie jest obiekt
buffer
. Odbiorca funkcji
zaleİy od sposobu jej wywoãania, a nie jest ona wywoãywana bezpoĤrednio
w tym miejscu. Zamiast tego zostaje ona przekazana do metody
forEach
, której
implementacja wywoãuje funkcjč w niedostčpnym dla programisty miejscu.
Okazuje sič, İe implementacja metody
forEach
jako domyĤlnego odbiorcy uİy-
wa obiektu globalnego. Poniewaİ obiekt globalny nie ma wãaĤciwoĤci
entries
,
przedstawiony kod zgãasza bãĈd. Na szczčĤcie metoda
forEach
umoİliwia poda-
nie opcjonalnego argumentu, okreĤlajĈcego odbiorcč wywoãania zwrotnego.
Dlatego moİna ãatwo rozwiĈzaþ problem:
var source = ["867", "-", "5309"];
source.forEach(buffer.add, buffer);
buffer.join(); // "867-5309"
Nie wszystkie funkcje wyİszego poziomu umoİliwiajĈ uİytkownikom okreĤle-
nie odbiorcy wywoãaę zwrotnych. Jak rozwiĈzaþ problem, gdyby metoda
forEach
nie przyjmowaãa dodatkowego argumentu okreĤlajĈcego odbiorcč? Dobrym
rozwiĈzaniem jest utworzenie funkcji lokalnej, która wywoãuje metodč
buffer.add
przy uİyciu odpowiedniej skãadni:
var source = ["867", "-", "5309"];
source.forEach(function(s) {
buffer.add(s);
});
buffer.join(); // "867-5309"
W tej wersji uİywana jest funkcja nakãadkowa, która bezpoĤrednio wywoãuje
add
jako metodč obiektu
buffer
. Zauwaİ, İe sama funkcja nakãadkowa w ogóle
nie uİywa sãowa kluczowego
this
. Niezaleİnie od sposobu wywoãania tej funkcji
nakãadkowej (moİna jĈ wywoãaþ jak zwykãĈ funkcjč, jak metodč innego
obiektu lub przy uİyciu instrukcji
call
) zawsze przekazuje ona argument do
docelowej tablicy.
Wersja funkcji wiĈİĈca odbiorcč z konkretnym obiektem jest tworzona tak czč-
sto, İe w standardzie ES5 dodano obsãugč tego wzorca w bibliotece. Obiekty
Sposób 26. Uİywaj metody bind do wiĈzania funkcji z podzbiorem argumentów
87
reprezentujĈce funkcje majĈ metodč
bind
, która przyjmuje obiekt odbiorcy
i generuje funkcjč nakãadkowĈ wywoãujĈcĈ pierwotnĈ funkcjč jako metodč
odbiorcy. Za pomocĈ metody
bind
moİna uproĤciþ przykãadowy kod:
var source = ["867", "-", "5309"];
source.forEach(buffer.add.bind(buffer));
buffer.join(); // "867-5309"
Pamičtaj, İe instrukcja
buffer.add.bind(buffer)
tworzy nowĈ funkcjč, zamiast
modyfikowaþ funkcjč
buffer.add
. Nowa funkcja dziaãa tak samo jak pierwotna,
ale jej odbiorca to obiekt
buffer
. Pierwotna funkcja pozostaje niezmieniona.
Oznacza to, İe:
buffer.add === buffer.add.bind(buffer); // false
Jest to subtelna, ale waİna róİnica. Oznacza to, İe metodč
bind
moİna bez-
piecznie wywoãaþ nawet dla funkcji uİywanych w innych miejscach pro-
gramu. Ma to znaczenie zwãaszcza w przypadku wspóãuİytkowanych metod
z prototypów. Taka metoda bčdzie dziaãaþ prawidãowo takİe dla obiektów po-
tomnych prototypu. Wičcej informacji o obiektach i prototypach znajdziesz
w rozdziale 4.
Co warto zapamičtaþ?
Q
Pamičtaj, İe pobranie metody nie prowadzi do ustawienia odbiorcy metody
na obiekt, z którego ona pochodzi.
Q
Przy przekazywaniu metody obiektu do funkcji wyİszego poziomu wykorzy-
staj funkcjč anonimowĈ, aby wywoãaþ metodč dla odpowiedniego odbiorcy.
Q
Stosuj metodč
bind
do szybkiego tworzenia funkcji zwiĈzanej z odpowiednim
odbiorcĈ.
Sposób 26. Uİywaj metody bind do wiĈzania funkcji z podzbiorem argumentów
Sposób 26. Uİywaj metody bind do wiĈzania funkcji
z podzbiorem argumentów (technika currying)
Metoda
bind
funkcji przydaje sič nie tylko do wiĈzania metod z odbiorcami.
WyobraĮ sobie prostĈ funkcjč tworzĈcĈ adresy URL na podstawie ich czčĤci
skãadowych.
function simpleURL(protocol, domain, path) {
return protocol + "://" + domain + "/" + path;
}
W programie potrzebne moİe byþ generowanie bezwzglčdnych adresów URL
na podstawie Ĥcieİek specyficznych dla witryny. Naturalnym sposobem na
wykonanie tego zadania jest uİycie metody
map
ze standardu ES5.
88
Rozdziaã 3. Korzystanie z funkcji
var urls = paths.map(function(path) {
return simpleURL("http", siteDomain, path);
});
Zauwaİ, İe w tej anonimowej funkcji w kaİdym powtórzeniu operacji przez
metodč
map
uİywane sĈ ten sam ãaęcuch znaków z protokoãem i ten sam ãaę-
cuch znaków z domenĈ witryny. Dwa pierwsze argumenty funkcji
simpleURL
sĈ niezmienne w kaİdej iteracji. Potrzebny jest tylko trzeci argument. Moİna
wykorzystaþ metodč
bind
funkcji
simpleURL
, aby automatycznie uzyskaþ po-
trzebnĈ funkcjč:
var urls = paths.map(simpleURL.bind(null, "http", siteDomain));
Wywoãanie
simpleURL.bind
tworzy nowĈ funkcjč, która deleguje zadania do funkcji
simpleURL
. Jak zawsze w pierwszym argumencie metody
bind
podawany jest od-
biorca. Poniewaİ funkcja
simpleURL
go nie potrzebuje, moİna podaþ tu dowolnĈ
wartoĤþ (standardowo uİywane sĈ wartoĤci
null
i
undefined
). Argumenty przeka-
zywane do funkcji
simpleURL
to wynik poãĈczenia pozostaãych argumentów
funkcji
simpleURL.bind
z argumentami przekazanymi do nowej funkcji. Ozna-
cza to, İe gdy funkcja utworzona za pomocĈ instrukcji
simpleURL.bind
jest
wywoãywana z jednym argumentem
path
, w wyniku oddelegowania zadania
wywoãanie wyglĈda tak:
simpleURL("http", siteDomain, path)
.
Technika wiĈzania funkcji z podzbiorem argumentów to currying (nazwa pocho-
dzi od logika Haskella Curry’ego, który spopularyzowaã tč metodč w matematy-
ce). Currying pozwala na zwičzãe implementowanie delegowania i nie wymaga
tak duİo szablonowego kodu jak jawne tworzenie funkcji nakãadkowych.
Co warto zapamičtaþ?
Q
Za pomocĈ metody
bind
moİna utworzyþ funkcjč delegujĈcĈ zadania, prze-
kazujĈcĈ staãy podzbiór wymaganych argumentów. Ta technika to currying.
Q
Aby za pomocĈ tej techniki utworzyþ funkcjč, która ignoruje odbiorcč,
jako reprezentujĈcy go argument podaj wartoĤþ
null
lub
undefined
.
Sposób 27. Wybieraj domkničcia zamiast ãaęcuchów znaków
do hermetyzowania kodu
Funkcje to wygodny sposób przechowywania kodu w postaci struktur danych
i póĮniejszego uruchamiania go. Pozwala to na stosowanie zwičzãych abstrak-
cyjnych instrukcji wyİszego poziomu, takich jak
map
i
forEach
, oraz jest istotĈ
asynchronicznego wykonywania operacji wejĤcia-wyjĤcia w JavaScripcie (zo-
bacz rozdziaã 7.). JednoczeĤnie moİna teİ zapisaþ kod jako ãaęcuch znaków
i przekazywaþ go do instrukcji
eval
. ProgramiĤci stojĈ wičc przed wyborem,
czy zapisaþ kod jako funkcjč, czy jako ãaęcuch znaków.
Sposób 27. Wybieraj domkničcia zamiast ãaęcuchów znaków do hermetyzowania kodu
89
Gdy masz wĈtpliwoĤci, stosuj funkcje. âaęcuchy znaków zapewniajĈ znacznie
mniejszĈ swobodč. Wynika to z waİnego powodu — nie sĈ domkničciami.
Przyjrzyj sič prostej funkcji wielokrotnie powtarzajĈcej okreĤlonĈ przez uİyt-
kownika operacjč:
function repeat(n, action) {
for (var i = 0; i < n; i++) {
eval(action);
}
}
W zasičgu globalnym ta funkcja dziaãa poprawnie, poniewaİ wszystkie refe-
rencje do zmiennych wystčpujĈce w ãaęcuchu znaków sĈ interpretowane
przez instrukcjč
eval
jako zmienne globalne. Na przykãad w skrypcie, który
mierzy szybkoĤþ dziaãania funkcji, moİna wykorzystaþ zmienne globalne
start
i
end
do przechowywania pomiarów czasu:
var start = [], end = [], timings = [];
repeat(1000,
"start.push(Date.now()); f(); end.push(Date.now())");
for (var i = 0, n = start.length; i < n; i++) {
timings[i] = end[i] - start[i];
}
Jednak ten skrypt jest podatny na problemy. Po przeniesieniu kodu do funkcji
start
i
end
nie bčdĈ juİ zmiennymi globalnymi:
function benchmark() {
var start = [], end = [], timings = [];
repeat(1000,
"start.push(Date.now()); f(); end.push(Date.now())");
for (var i = 0, n = start.length; i < n; i++) {
timings[i] = end[i] - start[i];
}
return timings;
}
Ta funkcja powoduje, İe instrukcja
repeat
wykorzystuje referencje do zmien-
nych globalnych
start
i
end
. W najlepszym przypadku jedna z tych zmien-
nych nie bčdzie istnieþ, a wywoãanie funkcji
benchmark
doprowadzi do bãčdu
ReferenceError
. JeĤli programista bčdzie miaã pecha, kod wywoãa instrukcjč
push
dla globalnych obiektów zwiĈzanych z nazwami
start
i
end
, a program
bčdzie dziaãaã nieprzewidywalnie.
Bardziej odporny na bãčdy interfejs API przyjmuje funkcjč zamiast ãaęcucha
znaków:
function repeat(n, action) {
for (var i = 0; i < n; i++) {
action();
}
}
90
Rozdziaã 3. Korzystanie z funkcji
Dzički temu w skrypcie
benchmark
moİna bezpiecznie uİywaþ zmiennych lo-
kalnych z domkničcia przekazywanego jako wielokrotnie uruchamiane wy-
woãanie zwrotne:
function benchmark() {
var start = [], end = [], timings = [];
repeat(1000, function() {
start.push(Date.now());
f();
end.push(Date.now());
});
for (var i = 0, n = start.length; i < n; i++) {
timings[i] = end[i] - start[i];
}
return timings;
}
Inny problem z instrukcjĈ
eval
polega na tym, İe silniki o wysokiej wydajnoĤci
majĈ zwykle wičksze problemy z optymalizacjĈ kodu z ãaęcuchów znaków,
poniewaİ kod Įródãowy moİe nie byþ dostčpny dla kompilatora na tyle wcze-
Ĥnie, by moİna byão w odpowiednim momencie przeprowadziþ optymalizacjč.
Wyraİenia funkcyjne moİna kompilowaþ jednoczeĤnie z kodem, w którym wy-
stčpujĈ. Dlatego znacznie ãatwiej sič je kompiluje w standardowy sposób.
Co warto zapamičtaþ?
Q
Nigdy nie stosuj lokalnych referencji w ãaęcuchach znaków przekazywanych
do interfejsu API, który wykonuje kod z ãaęcucha za pomocĈ instrukcji
eval
.
Q
Preferuj interfejsy API, które przyjmujĈ wywoãywane funkcje zamiast ãaęcu-
chów znaków przekazywanych do instrukcji
eval
.
Sposób 28. Unikaj stosowania metody toString funkcji
Funkcje w JavaScripcie majĈ niezwykãĈ cechč — umoİliwiajĈ wyĤwietlenie
swojego kodu Įródãowego jako ãaęcucha znaków:
(function(x) {
returnx + 1;
}).toString(); // "function (x) {\n return x + 1;\n}"
WyĤwietlanie kodu Įródãowego funkcji za pomocĈ mechanizmu refleksji daje
duİo moİliwoĤci, a pomysãowi hakerzy potrafiĈ znaleĮþ ciekawe sposoby ich
wykorzystania. Jednak metoda
toString
funkcji ma powaİne ograniczenia.
Przede wszystkim standard ECMAScript nie okreĤla İadnych wymagaę wobec
ãaęcuchów znaków zwracanych przez metodč
toString
funkcji. To oznacza,
İe róİne silniki JavaScriptu mogĈ zwracaþ odmienne ãaęcuchy znaków.
Moİliwe nawet, İe zwrócony tekst w rzeczywistoĤci nie bčdzie podobny do
kodu danej funkcji.
Sposób 28. Unikaj stosowania metody toString funkcji
91
W praktyce silniki JavaScriptu próbujĈ wyĤwietliþ wiernĈ reprezentacjč kodu
Įródãowego funkcji, o ile napisano jĈ w czystym JavaScripcie. Nie sprawdza
sič to na przykãad dla funkcji generowanych przez wbudowane biblioteki
Ĥrodowiska hosta:
(function(x) {
returnx + 1;
}).bind(16).toString(); // "function (x) {\n [native code]\n}"
Poniewaİ w wielu Ĥrodowiskach hosta funkcja
bind
jest zaimplementowana
w innym jčzyku programowania (zwykle w C++), zwracana jest skompilo-
wana funkcja bez kodu Įródãowego w JavaScripcie, który Ĥrodowisko mo-
gãoby wyĤwietliþ.
Poniewaİ przeglĈdarki wedãug standardu mogĈ w odpowiedzi na wywoãanie
funkcji
toString
zwracaþ inne dane, zbyt ãatwo jest napisaþ program, który
dziaãa prawidãowo w jednym systemie, ale niepoprawnie w innym. Nawet
drobne rozbieİnoĤci w implementacjach JavaScriptu (na przykãad sposób for-
matowania odstčpów) mogĈ zaburzyþ dziaãanie programu, który jest wraİliwy
na szczegóãy zapisu kodu Įródãowego funkcji.
Ponadto kod Įródãowy generowany przez metodč
toString
nie zwraca repre-
zentacji domkničcia z zachowaniem wartoĤci zwiĈzanych ze zmiennymi
wewnčtrznymi. Oto przykãad:
(function(x) {
return function(y) {
return x + y;
}
})(42).toString(); // "function (y) {\n return x + y;\n}"
Zauwaİ, İe w wynikowym ãaęcuchu znaków wystčpuje zmienna
x
, choþ
funkcja jest domkničciem wiĈİĈcym
x
z wartoĤciĈ
42
.
Te ograniczenia sprawiajĈ, İe trudno jest w przydatny i niezawodny sposób
pobieraþ kod Įródãowy funkcji. Dlatego zwykle warto unikaþ opisanej techniki.
Do bardzo zaawansowanych operacji pobierania kodu Įródãowego funkcji
naleİy stosowaþ starannie napisane parsery i biblioteki przetwarzajĈce
kod w JavaScripcie. W razie wĈtpliwoĤci najbezpieczniej jest traktowaþ
funkcje JavaScriptu jak abstrakcyjne struktury, których nie naleİy dzieliþ
na fragmenty.
Co warto zapamičtaþ?
Q
Silniki JavaScriptu nie muszĈ wiernie zwracaþ kodu Įródãowego funkcji
po wywoãaniu metody
toString
.
Q
Nigdy nie polegaj na szczegóãach z pobranego kodu Įródãowego funkcji,
poniewaİ wywoãanie metody
toString
w róİnych silnikach moİe dawaþ od-
mienne wyniki.
92
Rozdziaã 3. Korzystanie z funkcji
Q
Tekst zwracany przez metodč
toString
nie pokazuje wartoĤci zmiennych
lokalnych z domkničcia.
Q
Zwykle warto unikaþ wywoãywania metody
toString
dla funkcji.
Sposób 29. Unikaj niestandardowych wãaĤciwoĤci
przeznaczonych do inspekcji stosu
Wiele Ĥrodowisk JavaScriptu w przeszãoĤci udostčpniaão mechanizmy do in-
spekcji stosu wywoãaę, czyli ãaęcucha obecnie wykonywanych aktywnych
funkcji (wičcej o stosie wywoãaę dowiesz sič ze sposobu 64.). W starszych
Ĥrodowiskach hosta kaİdy obiekt
arguments
ma dwie dodatkowe wãaĤciwoĤci:
arguments.callee
(okreĤla funkcjč wywoãanĈ z argumentami
arguments
) i
arguments.
´
caller
(okreĤla funkcjč wywoãujĈcĈ). Pierwsza z tych wãaĤciwoĤci nadal jest
obsãugiwana w wielu Ĥrodowiskach, jednak sãuİy tylko do rekurencyjnego
wskazywania funkcji anonimowych w nich samych.
var factorial = (function(n) {
return (n <= 1) ? 1 : (n * arguments.callee(n - 1));
});
Nie jest to specjalnie przydatne, poniewaİ ãatwiej jest w funkcji wywoãaþ jĈ
za pomocĈ nazwy.
function factorial(n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
WãaĤciwoĤþ
arguments.caller
daje wičksze moİliwoĤci. Wskazuje funkcjč, w której
znalazão sič wywoãanie z danym obiektem
arguments
. Z powodów bezpieczeę-
stwa mechanizm ten zostaã usuničty z wičkszoĤci Ĥrodowisk, tak wičc moİe
okazaþ sič niedostčpny. Wiele Ĥrodowisk JavaScriptu udostčpnia podobnĈ
wãaĤciwoĤþ dla obiektów funkcyjnych — niestandardowĈ, ale czčsto spotyka-
nĈ wãaĤciwoĤþ
caller
. OkreĤla ona jednostkč, która wywoãaãa danĈ funkcjč.
function revealCaller() {
return revealCaller.caller;
}
function start() {
return revealCaller();
}
start() === start; // true
Dobrym pomysãem moİe wydawaþ sič wykorzystanie tej wãaĤciwoĤci do pobie-
rania Ĥladu stosu (struktury danych zawierajĈcej obecny stan stosu wywoãaę).
Budowanie Ĥladu stosu na pozór jest bardzo proste.
function getCallStack() {
var stack = [];
Sposób 29. Unikaj niestandardowych wãaĤciwoĤci przeznaczonych do inspekcji stosu
93
for (var f = getCallStack.caller; f; f = f.caller) {
stack.push(f);
}
return stack;
}
Dla prostych stosów wywoãaę funkcja
getCallStack
dziaãa poprawnie.
function f1() {
return getCallStack();
}
function f2() {
return f1();
}
var trace = f2();
trace; // [f1, f2]
Jednak dziaãanie funkcji
getCallStack
ãatwo jest zakãóciþ. JeĤli dana funkcja
wystčpuje w stosie wywoãaę wičcej niİ raz, kod odpowiedzialny za inspekcjč
stosu wpada w pčtlč.
function f(n) {
return n === 0 ? getCallStack() : f(n - 1);
}
var trace = f(1); // Pętla nieskończona
W czym tkwi problem? Poniewaİ funkcja
f
rekurencyjnie wywoãuje samĈ siebie,
wãaĤciwoĤþ
caller
jest automatycznie ustawiana na
f
. Dlatego pčtla w funkcji
getCallStack
nieustannie szuka funkcji
f
. Nawet jeĤli programista spróbuje
wykrywaþ takie cykle, niedostčpne bčdĈ informacje o tym, jaka funkcja wy-
woãaãa funkcjč
f
, zanim funkcja
f
wywoãaãa samĈ siebie. Informacje o reszcie
stosu wywoãaę zostajĈ wičc utracone.
Wszystkie wymienione mechanizmy inspekcji stosu sĈ niestandardowe oraz
majĈ ograniczonĈ przenoĤnoĤþ i zastosowania. Ponadto w standardzie ES5
sĈ one niedozwolone w funkcjach w trybie strict. Próby dostčpu do wãaĤciwoĤci
caller
i
callee
funkcji w trybie strict oraz do obiektów
arguments
powodujĈ bãĈd.
function f() {
"use strict";
return f.caller;
}
f(); // Błąd — nie można używać właściwości caller funkcji w trybie strict
Najlepsze podejĤcie polega na rezygnacji z inspekcji stosu. JeĤli potrzebujesz
sprawdzaþ stos na potrzeby debugowania, znacznie lepiej jest uİyþ interak-
tywnego debugera.
94
Rozdziaã 3. Korzystanie z funkcji
Co warto zapamičtaþ?
Q
Unikaj niestandardowych wãaĤciwoĤci
arguments.caller
i
arguments.callee
,
poniewaİ w niektórych Ĥrodowiskach sĈ niedostčpne.
Q
Unikaj niestandardowej wãaĤciwoĤci
caller
funkcji, poniewaİ nie zawsze
zwraca kompletne informacje o stosie.
Skorowidz
A
abstrakcje wyİszego poziomu, 76
aktor, 112
API, 151
argument, 81
arnoĤþ funkcji, 79
ASCII, 43
aspekty pragmatyczne, 13
asynchroniczne
pčtle, 190
wywoãania zwrotne, 201
atrapa, 167
atrybut, 135
automatyczne dodawanie Ĥredników, 37
B
bezstanowy interfejs API, 161
biblioteka, 151
Canvas, 161
bitowe
operatory arytmetyczne, 28
wyraİenie OR, 26
blok catch, 59
blokowanie kolejki zdarzeę, 193
bãĈd
parsowania, 38, 42
skãadni, 66
TypeError, 101
bãčdy ignorowane, 187
BMP, Basic Multilingual Plane, 44
C
currying, 87, 88
D
debugowanie, 63, 64
dodawanie argumentów, 157
domkničcia, 54, 88
dziedziczenie
implementacji, 95
po klasach standardowych, 117
E
ECMA, 13
ECMAScript, 14
enumeracja, 136
F
format MediaWiki, 165
funkcja, 71
Alert, 160
averageScore, 48
benchmark, 89
downloadAllAsync, 186, 188, 199
downloadAsync, 182, 184
downloadCachingAsync, 202
downloadOneAsync, 192
eval, 67–69
extend, 160
fail, 39
getCallStack, 93
isNaN, 29
make, 55
MEDIAWIKI, 165
negative, 192
next, 196
Number, 34
onsuccess, 198
208
Skorowidz
funkcja
sandwichMaker, 55
score, 48
select, 206
showContents, 185
simpleURL, 88
status, 53
trimSections, 58
tryNextURL, 191
User, 96, 102
funkcje
asynchroniczne, 180
blokujĈce, 180
rekurencyjne, 193
synchroniczne, 180
wariadyczne, 79, 81
wywoãywane zwrotnie, 74
wyİszego poziomu, 74
G
graf
sceny, 111
sieci spoãecznoĤciowej, 137
H
hermetyzowanie kodu, 88
hierarchia dziedziczenia, 115
hoisting, 57
zmiennych, 58
I
idiom, 151
IIFE, 23, 40, 61
implementowanie sãowników, 123
instrukcja
__proto__, 95
apply, 79
bind, 85
break, 42
call, 77
continue, 42
eval, 90
getPrototypeOf, 95
if, 188
new, 101
Object.getPrototypeOf, 99
prototype, 95
return, 57
throw, 42
var, 39
with, 52, 53
interfejs API, 151
interfejsy
bezstanowe, 161
stanowe, 161
elastyczne, 164
introspekcja, 119
iterator, 84
J
jawna konwersja, 36
jednostki kodowe, 43
K
klasa, 98
Array, 118, 146
Dict, 124
MWPage, 165
User, 106
kodowanie
o zmiennej dãugoĤci, 45
znaków, 43
koercja, 171
kolejka zdarzeę, 179, 193
kolekcja, 123
kolekcje uporzĈdkowane, 132
konstrukcja try…catch, 59
konstruktor, 71
klasy Array, 148
kontekst, 112
konwencje spójne, 151
konwersja typu, 28
jawna, 36
niejawna, 35
L
liczba argumentów, 79
liczby
o podwójnej precyzji, 26
zmiennoprzecinkowe, 24
literaãy tablicowe, 148
â
ãaęcuch
zasičgu, 52
znaków, 30, 32, 43–45
metod, 174
Skorowidz
209
ãĈczenie
metod w ãaęcuch, 175
obietnic, 204
plików, 23
ãĈcznoĤþ lewostronna, 28
M
mapy deskryptorów wãaĤciwoĤci, 126
mechanizm naprawiania bãčdów, 38
metadane, 135
metoda, 71
bind, 87
call, 77, 82
concat, 147
enable, 168, 172
forEach, 78, 146
hasOwnProperty, 128
inNetwork, 195
Object.create, 102, 114
pick, 139
postMessage, 194
replace, 174
shift, 82
slice, 83, 148
toString, 25, 30, 90
toUpperCase, 33
valueOf, 30, 31, 34
metody
dla niestandardowego odbiorcy, 77
do obsãugi iteracji, 142
klasy Array, 146, 148
niedeterministyczne, 139
o staãym odbiorcy, 85
w prototypach, 103
modyfikowanie
obiektu, 82, 136
wãaĤciwoĤci __proto__, 100
monkey patching, 120
N
nakãadki obiektowe, 32
naprawianie bãčdów, 38
narzčdzie lint, 51
nazwa wãaĤciwoĤci, 115
niejawna konwersja typu, 27
niejawne
przenoszenie deklaracji zmiennych,
57
tworzenie nakãadek obiektowych, 34
wiĈzanie obiektu, 109
nieprzenoĤne okreĤlanie zasičgu, 62, 65
niestandardowy odbiorca, 77
O
obiekt, 95
arguments, 82, 84
this, 109, 177
typu String, 32
XMLHttpRequest, 182
obiekty
globalne, 48
z opcjami, 157
obietnice, 203
obsãuga
bãčdów, 187
iteracji, 142
ãaęcuchów metod, 174
ograniczone konstrukcje, 41
okreĤlanie typu na podstawie struktury,
166
operacja ãĈczna lewostronnie, 28
operator
+, 30
++, 42
identycznoĤci, 34
równoĤci, 33, 35
typeof, 32
undefined, 32
operatory
arytmetyczne bitowe, 28
przesuničcia, 28
osadzany jčzyk skryptowy, 179
P
parametr formalny, 81
pary surogatów, 46
pčtla
for, 60, 140, 142
for…in, 134, 135, 140
while, 140
zdarzeę, 179, 181
pliki o róİnych trybach, 23
pobieranie
metod, 85
prototypów obiektów, 97
podstawa, 25
podwójna precyzja, 25
praca na odlegãoĤþ, 120
prawo ãĈcznoĤci, 26
predykat, 143
210
Skorowidz
programowanie
asynchroniczne, 187
defensywne, 172
obiektowe, 71
prototyp, 95, 119
Array.prototype, 126
Object.prototype, 134
prototypy null, 126
przechowywanie
metod
w egzemplarzach, 104
w prototypie, 105
prywatnych danych, 105
stanu egzemplarza, 107
w egzemplarzach, 108
w prototypie, 108
przeciĈİony operator, 30
przekazywanie argumentów, 157
przepeãnienie stosu, 192
przesãanianie konstruktora, 103
przestrzeę nazw, 48
przetwarzanie skrócone, 145
R
referencje do obiektu arguments, 84
rekurencja, 190
S
scalanie skryptów, 22, 24
semantyka, 13
skãadnia, 13
sãowniki, 123
sãowo kluczowe
arguments, 81
const, 20
new, 101
return, 41
this, 72, 86
var, 64
with, 51
standard
ECMAScript, 13, 19
IEEE, 29
stanowe interfejsy API, 162
stos wywoãaę, 92, 191, 193
stosowanie
domkničþ, 105
enumerowanych wãaĤciwoĤci, 134
instrukcji Object.getPrototypeOf, 99
metody toString, 90
nazwanych wyraİeę funkcyjnych, 62
rekurencji, 190
tablic, 132
techniki monkey patching, 120
struktura, 164
styl pãynny, 176
systemy modularne, 23
ģ
Ĥlad stosu, 93
Ĥrednik, 37
Ĥrodowisko leksykalne, 52
T
tablice, 123
technika monkey patching, 120
tryb strict, 21, 23
tworzenie
abstrakcji, 76
asynchronicznych pčtli, 190
funkcji wariadycznych, 81
kolekcji, 132
pčtli, 145
zmiennych globalnych, 47
zmiennych lokalnych, 48
typy
oparte na strukturze, 169
proste, 32
U
UCS-2, 43
Unicode, 43
UTF-16, 45
W
wartoĤci
logiczne, 31
oznaczajĈce faãsz, 31
wartoĤþ
NaN, 29
null, 28, 63
ostateczna, 204
scores.length, 141
true, 31
undefined, 32, 153, 159
wĈtek, 180
wektor bitowy, 167
wersja JavaScriptu, 19
Skorowidz
211
wiĈzanie funkcji, 87, 88
wãaĤciwoĤci
enumerowane, 134
wewnčtrzne, 117
wãaĤciwoĤþ
__proto__, 97, 100, 127, 130
caller, 94
info, 53
length, 168
prototype, 96
wspóãbieİne pobieranie plików, 197
wspóãbieİnoĤþ, 14
oparta na pčtli zdarzeę, 179
wspóãrzčdna kodowa znaku, 43
wspóãrzčdne kodowe Unicode, 46
wykonywanie funkcji rekurencyjnej,
193
wypeãniacz, 121
wyraİenia
funkcyjne, 23, 56
anonimowe, 64
natychmiast wywoãywane, 61
nazwane, 62
IIFE, 59
wyraİenie OR, 25
wyĤcig do danych, 199
wywoãania
asynchroniczne, 183
bezpoĤrednie, 70
funkcji, 71, 79
konstruktorów, 71
metod, 71, 77
poĤrednie, 70
zwrotne, 183
nazwane, 183
zagnieİdİone, 183
wywoãanie downloadAsync, 183
Z
zagnieİdİanie, 184
deklaracji funkcji, 65
zasičg
blokowy, 57
leksykalny, 57
lokalny, 59
zmiennych, 47
zaĤmiecanie przez prototypy, 125, 126,
128
zbiór ãaęcuchów znaków, 168
zdarzenia, 180
zmienna arguments, 21
zmienne
globalne, 23, 47
lokalne, 50, 67
znak
(, 38
., 46
/, 39
[, 38
;, 41
znaki niebezpieczne, 40
zwracanie obiektu this, 177