Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00373 010915 10755425 na godz. na dobę w sumie
Efektywny JavaScript. 68 sposobów wykorzystania potęgi języka - ebook/pdf
Efektywny JavaScript. 68 sposobów wykorzystania potęgi języka - ebook/pdf
Autor: Liczba stron: 216
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-1421-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> webmasterstwo >> javascript - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

68 sposobów na wykorzystanie możliwości JavaScriptu

JavaScript jeszcze do niedawna kojarzył się głównie ze stronami oraz aplikacjami internetowymi, a jego głównym zastosowaniem były operacje na drzewie DOM. Jednak te czasy mijają, a język ten jest coraz chętniej wykorzystywany również po stronie serwera. JavaScript jako pełnoprawny język programowania? Oczywiście! W dodatku okazuje się, że może on być bardzo wydajny, elastyczny i przyjazny dla programistów — wystarczy przestrzegać kilku zasad!

Te tajemnicze zasady zostały zebrane w niniejszej książce. Jeśli będziesz o nich pamiętać, wykorzystasz w pełni potencjał JavaScriptu. W trakcie lektury dowiesz się, jak najlepiej deklarować zmienne, używać funkcji oraz radzić sobie z obiektami i prototypami. W kolejnych rozdziałach nauczysz się budować przyjazne API oraz korzystać ze słowników i tablic. Na sam koniec zdobędziesz informacje, które mają kluczowe znaczenie w przypadku programowania współbieżnego. Jeżeli jesteś programistą języka JavaScript, jeżeli chcesz poprawić swoje umiejętności programowania w tym języku, jest to dla Ciebie lektura obowiązkowa. Przekonaj się, jak przyjemne i wydajne może być programowanie w JavaScripcie!

Oto kluczowe obszary poruszane w książce:

Książka jest podzielona na wzbogacone przykładami opisy 68 sprawdzonych metod pisania lepszego kodu w JavaScripcie. Dowiesz się tu, jak wybrać odpowiedni styl programowania dla poszczególnych projektów, radzić sobie z nieoczekiwanymi problemami i z powodzeniem pracować z wszystkimi aspektami JavaScriptu — od struktur danych po mechanizmy współbieżne.

Najlepsze porady dotyczące JavaScriptu dla każdego programisty!

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

Darmowy fragment publikacji:

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. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis tre(cid:292)ci Przedmowa .................................................................................... 11 Wprowadzenie ................................................................................ 13 Podzi(cid:269)kowania ............................................................................... 15 O autorze ....................................................................................... 17 Rozdzia(cid:227) 1. Przyzwyczajanie si(cid:269) do JavaScriptu ............................. 19 Sposób 1. Ustal, której wersji JavaScriptu u(cid:304)ywasz...................................... 19 Sposób 2. Liczby zmiennoprzecinkowe w JavaScripcie.................................. 24 Sposób 3. Uwa(cid:304)aj na niejawn(cid:264) konwersj(cid:269) typu............................................. 27 Sposób 4. Stosuj typy proste zamiast nak(cid:227)adek obiektowych........................ 32 Sposób 5. Unikaj stosowania operatora == dla warto(cid:292)ci o ró(cid:304)nych typach .... 34 Sposób 6. Ograniczenia mechanizmu automatycznego dodawania (cid:292)redników .................................................................. 37 Sposób 7. Traktuj (cid:227)a(cid:281)cuchy znaków jak sekwencje 16-bitowych jednostek kodowych................................................. 43 Rozdzia(cid:227) 2. Zasi(cid:269)g zmiennych ......................................................... 47 Sposób 8. Minimalizuj liczb(cid:269) obiektów globalnych....................................... 47 Sposób 9. Zawsze deklaruj zmienne lokalne ................................................ 50 Sposób 10. Unikaj s(cid:227)owa kluczowego with.................................................... 51 Sposób 11. Poznaj domkni(cid:269)cia ..................................................................... 54 Sposób 12. Niejawne przenoszenie deklaracji zmiennych na pocz(cid:264)tek bloku (czyli hoisting)............................................... 57 Sposób 13. Stosuj wyra(cid:304)enia IIFE do tworzenia zasi(cid:269)gu lokalnego ................ 59 Poleć książkęKup książkę 8 Spis tre(cid:292)ci Sposób 14. Uwa(cid:304)aj na nieprzeno(cid:292)ne okre(cid:292)lanie zasi(cid:269)gu nazwanych wyra(cid:304)e(cid:281) funkcyjnych............................................... 62 Sposób 15. Uwa(cid:304)aj na nieprzeno(cid:292)ne okre(cid:292)lanie zasi(cid:269)gu lokalnych deklaracji funkcji w bloku ......................................................... 65 Sposób 16. Unikaj tworzenia zmiennych lokalnych za pomoc(cid:264) funkcji eval .............................................................. 67 Sposób 17. Przedk(cid:227)adaj po(cid:292)rednie wywo(cid:227)ania eval nad bezpo(cid:292)rednie wywo(cid:227)ania tej funkcji ..................................... 68 Rozdzia(cid:227) 3. Korzystanie z funkcji.....................................................71 Sposób 18. Ró(cid:304)nice mi(cid:269)dzy wywo(cid:227)aniami funkcji, metod i konstruktorów..... 71 Sposób 19. Funkcje wy(cid:304)szego poziomu ........................................................ 74 Sposób 20. Stosuj instrukcj(cid:269) call do wywo(cid:227)ywania metod dla niestandardowego odbiorcy.................................................. 77 Sposób 21. Stosuj instrukcj(cid:269) apply do wywo(cid:227)ywania funkcji o ró(cid:304)nej liczbie argumentów....................................................... 79 Sposób 22. Stosuj s(cid:227)owo kluczowe arguments do tworzenia funkcji wariadycznych........................................................................... 81 Sposób 23. Nigdy nie modyfikuj obiektu arguments ..................................... 82 Sposób 24. U(cid:304)ywaj zmiennych do zapisywania referencji do obiektu arguments................................................................ 84 Sposób 25. U(cid:304)ywaj instrukcji bind do pobierania metod o sta(cid:227)ym odbiorcy... 85 Sposób 26. U(cid:304)ywaj metody bind do wi(cid:264)zania funkcji z podzbiorem argumentów (technika currying).............................. 87 Sposób 27. Wybieraj domkni(cid:269)cia zamiast (cid:227)a(cid:281)cuchów znaków do hermetyzowania kodu........................................................... 88 Sposób 28. Unikaj stosowania metody toString funkcji ................................ 90 Sposób 29. Unikaj niestandardowych w(cid:227)a(cid:292)ciwo(cid:292)ci przeznaczonych do inspekcji stosu...................................................................... 92 Rozdzia(cid:227) 4. Obiekty i prototypy .......................................................95 Sposób 30. Ró(cid:304)nice mi(cid:269)dzy instrukcjami prototype, getPrototypeOf i __proto__.......................................................... 95 Sposób 31. Stosuj instrukcj(cid:269) Object.getPrototypeOf zamiast __proto__ ......... 99 Sposób 32. Nigdy nie modyfikuj w(cid:227)a(cid:292)ciwo(cid:292)ci __proto__............................... 100 Sposób 33. Uniezale(cid:304)nianie konstruktorów od instrukcji new .................... 101 Sposób 34. Umieszczaj metody w prototypach............................................ 103 Sposób 35. Stosuj domkni(cid:269)cia do przechowywania prywatnych danych ..... 105 Sposób 36. Stan egzemplarzy przechowuj tylko w nich samych.................. 107 Sposób 37. Zwracaj uwag(cid:269) na niejawne wi(cid:264)zanie obiektu this.................... 109 Poleć książkęKup książkę Spis tre(cid:292)ci 9 Sposób 38. Wywo(cid:227)ywanie konstruktorów klasy bazowej w konstruktorach klas pochodnych ......................................... 111 Sposób 39. Nigdy nie wykorzystuj ponownie nazw w(cid:227)a(cid:292)ciwo(cid:292)ci z klasy bazowej........................................................................ 115 Sposób 40. Unikaj dziedziczenia po klasach standardowych....................... 117 Sposób 41. Traktuj prototypy jak szczegó(cid:227) implementacji ........................... 119 Sposób 42. Unikaj nieprzemy(cid:292)lanego stosowania techniki monkey patching ..................................................................... 120 Rozdzia(cid:227) 5. Tablice i s(cid:227)owniki.........................................................123 Sposób 43. Budowanie prostych s(cid:227)owników na podstawie egzemplarzy typu Object..................................... 123 Sposób 44. Stosuj prototypy null, aby unikn(cid:264)(cid:254) za(cid:292)miecania przez prototypy........................................................................ 126 Sposób 45. U(cid:304)ywaj metody hasOwnProperty do zabezpieczania si(cid:269) przed za(cid:292)miecaniem przez prototypy Sposób 46. Stosuj tablice zamiast s(cid:227)owników przy tworzeniu kolekcji uporz(cid:264)dkowanych ............................... 132 Sposób 47. Nigdy nie dodawaj enumerowanych w(cid:227)a(cid:292)ciwo(cid:292)ci do prototypu Object.prototype ................................................. 134 Sposób 48. Unikaj modyfikowania obiektu w trakcie enumeracji................ 136 Sposób 49. Stosuj p(cid:269)tl(cid:269) for zamiast p(cid:269)tli for…in przy przechodzeniu po tablicy.................................................. 140 Sposób 50. Zamiast p(cid:269)tli stosuj metody do obs(cid:227)ugi iteracji ......................... 142 Sposób 51. Wykorzystaj uniwersalne metody klasy Array w obiektach podobnych do tablic ............................................. 146 Sposób 52. Przedk(cid:227)adaj litera(cid:227)y tablicowe nad konstruktor klasy Array ...... 148 Rozdzia(cid:227) 6. Projekty bibliotek i interfejsów API ..............................151 Sposób 53. Przestrzegaj spójnych konwencji .............................................. 151 Sposób 54. Traktuj warto(cid:292)(cid:254) undefined jak brak warto(cid:292)ci............................ 153 Sposób 55. Stosuj obiekty z opcjami do przekazywania argumentów za pomoc(cid:264) s(cid:227)ów kluczowych..................................................... 157 Sposób 56. Unikaj niepotrzebnego przechowywania stanu ........................... 161 Sposób 57. Okre(cid:292)laj typy na podstawie struktury, aby tworzy(cid:254) elastyczne interfejsy.............................................. 164 Sposób 58. Ró(cid:304)nice mi(cid:269)dzy tablicami a obiektami podobnymi do tablic ...... 167 Sposób 59. Unikaj nadmiernej koercji........................................................ 171 Sposób 60. Obs(cid:227)uga (cid:227)a(cid:281)cuchów metod....................................................... 174 Poleć książkęKup książkę 10 Spis tre(cid:292)ci Rozdzia(cid:227) 7. Wspó(cid:227)bie(cid:304)no(cid:292)(cid:254) ............................................................. 179 Sposób 61. Nie blokuj kolejki zdarze(cid:281) operacjami wej(cid:292)cia-wyj(cid:292)cia .............. 180 Sposób 62. Stosuj zagnie(cid:304)d(cid:304)one lub nazwane wywo(cid:227)ania zwrotne do tworzenia sekwencji asynchronicznych wywo(cid:227)a(cid:281) ................. 183 Sposób 63. Pami(cid:269)taj o ignorowanych b(cid:227)(cid:269)dach ............................................ 187 Sposób 64. Stosuj rekurencj(cid:269) do tworzenia asynchronicznych p(cid:269)tli............ 190 Sposób 65. Nie blokuj kolejki zdarze(cid:281) obliczeniami .................................... 193 Sposób 66. Wykorzystaj licznik do wykonywania wspó(cid:227)bie(cid:304)nych operacji ... 197 Sposób 67. Nigdy nie uruchamiaj synchronicznie asynchronicznych wywo(cid:227)a(cid:281) zwrotnych ................................................................. 201 Sposób 68. Stosuj obietnice, aby zwi(cid:269)kszy(cid:254) przejrzysto(cid:292)(cid:254) asynchronicznego kodu ...................................... 203 Skorowidz .................................................................................... 207 Poleć książkęKup książkę Korzystanie z funkcji Funkcje to „wo(cid:227)y robocze” JavaScriptu. S(cid:264) dla programistów jednocze(cid:292)nie podstawow(cid:264) abstrakcj(cid:264) i mechanizmem implementacyjnym. Funkcje odgry- waj(cid:264) tu role, które w innych j(cid:269)zykach s(cid:264) przypisane do wielu ró(cid:304)nych elemen- tów: procedur, metod, konstruktorów, a nawet klas i modu(cid:227)ów. Gdy zapoznasz si(cid:269) z subtelnymi aspektami funkcji, opanujesz istotn(cid:264) cz(cid:269)(cid:292)(cid:254) JavaScriptu. Pami(cid:269)taj jednak o tym, (cid:304)e nauka efektywnego pos(cid:227)ugiwania si(cid:269) funkcjami w ró(cid:304)nych kontekstach wymaga czasu. Sposób 18. Ró(cid:304)nice mi(cid:269)dzy wywo(cid:227)aniami funkcji, metod i konstruktorów Je(cid:292)li programowanie obiektowe nie jest Ci obce, prawdopodobnie traktujesz funkcje, metody i konstruktory klas jako trzy odr(cid:269)bne elementy. W Java- Scripcie odpowiadaj(cid:264) im trzy ró(cid:304)ne sposoby korzystania z jednej konstrukcji — z funkcji. Najprostszy sposób to wywo(cid:227)anie funkcji: function hello(username) { return Witaj, + username; } hello( Keyser Söze ); // Witaj, Keyser Söze Ten kod dzia(cid:227)a w standardowy sposób — wywo(cid:227)uje funkcj(cid:269) hello i wi(cid:264)(cid:304)e pa- rametr name z podanym argumentem. Metody w JavaScripcie to nale(cid:304)(cid:264)ce do obiektów w(cid:227)a(cid:292)ciwo(cid:292)ci, które s(cid:264) funk- cjami: var obj = { hello: function() { return Witaj, + this.username; }, Poleć książkęKup książkę 72 Rozdzia(cid:227) 3. Korzystanie z funkcji username: Hans Gruber }; obj.hello(); // Witaj, Hans Gruber Zauwa(cid:304), (cid:304)e w metodzie hello u(cid:304)ywane jest s(cid:227)owo this, aby uzyska(cid:254) dost(cid:269)p do obiektu obj. Mo(cid:304)e si(cid:269) wydawa(cid:254), (cid:304)e this zostaje zwi(cid:264)zane z obj, poniewa(cid:304) metod(cid:269) hello zdefiniowano w(cid:227)a(cid:292)nie w obiekcie obj. Jednak mo(cid:304)na skopiowa(cid:254) referencj(cid:269) do tej samej funkcji w innym obiekcie i uzyska(cid:254) odmienny wynik: var obj2 = { hello: obj.hello, username: Boo Radley }; obj2.hello(); // Witaj, Boo Radley W wywo(cid:227)aniu metody samo wywo(cid:227)anie okre(cid:292)la, z czym zwi(cid:264)zane jest s(cid:227)owo this (wyznacza ono odbiorc(cid:269) wywo(cid:227)ania). Wyra(cid:304)enie obj.hello() powoduje wyszukanie w(cid:227)a(cid:292)ciwo(cid:292)ci hello obiektu obj i wywo(cid:227)anie jej dla odbiorcy obj. Wyra(cid:304)enie obj2.hello() prowadzi do wyszukiwania w(cid:227)a(cid:292)ciwo(cid:292)ci hello obiektu obj2; t(cid:264) w(cid:227)a(cid:292)ciwo(cid:292)ci(cid:264) jest ta sama funkcja co w wywo(cid:227)aniu obj.hello, tu jednak jest ona wywo(cid:227)ywana dla odbiorcy obj2. Wywo(cid:227)anie metody obiektu stan- dardowo prowadzi do wyszukania metody i u(cid:304)ycia danego obiektu jako od- biorcy tej metody. Poniewa(cid:304) metody to funkcje wywo(cid:227)ane dla okre(cid:292)lonego obiektu, mo(cid:304)na swo- bodnie wywo(cid:227)ywa(cid:254) zwyk(cid:227)e funkcje z wykorzystaniem s(cid:227)owa kluczowego this: function hello() { return Witaj, + this.username; } Takie rozwi(cid:264)zanie mo(cid:304)e okaza(cid:254) si(cid:269) przydatne, je(cid:292)li chcesz wst(cid:269)pnie zdefi- niowa(cid:254) funkcj(cid:269), która b(cid:269)dzie u(cid:304)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(cid:264)ca s(cid:227)owo kluczowe this nie jest przydatna, je(cid:292)li chcesz j(cid:264) wywo(cid:227)ywa(cid:254) jak zwyk(cid:227)(cid:264) funkcj(cid:269), a nie jak metod(cid:269): hello(); // Witaj, undefined Nie pomaga to, (cid:304)e w zwyk(cid:227)ych wywo(cid:227)aniach funkcji (nie w metodach) odbior- c(cid:264) jest obiekt globalny, który tu nie ma w(cid:227)a(cid:292)ciwo(cid:292)ci o nazwie name i zwraca warto(cid:292)(cid:254) undefined. Wywo(cid:227)anie metody jako funkcji rzadko jest przydatne, je(cid:292)li Poleć książkęKup książkę Sposób 18. Ró(cid:304)nice mi(cid:269)dzy wywo(cid:227)aniami funkcji, metod i konstruktorów 73 dana metoda wykorzystuje s(cid:227)owo kluczowe this. Przyczyn(cid:264) jest to, (cid:304)e trudno oczekiwa(cid:254), i(cid:304) obiekt globalny b(cid:269)dzie mia(cid:227) te same w(cid:227)a(cid:292)ciwo(cid:292)ci co obiekt, dla którego napisano dan(cid:264) metod(cid:269). U(cid:304)ywanie w tym kontek(cid:292)cie obiektu global- nego jest na tyle problematycznym rozwi(cid:264)zaniem domy(cid:292)lnym, (cid:304)e w trybie strict w standardzie ES5 this jest domy(cid:292)lnie wi(cid:264)zane z warto(cid:292)ci(cid:264) 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(cid:254) przypadkowe wykorzystanie metod jako zwyk(cid:227)ych funkcji. Kod szybko przestanie wtedy dzia(cid:227)a(cid:254), poniewa(cid:304) próba dost(cid:269)pu do w(cid:227)a(cid:292)ciwo(cid:292)ci obiektu undefined spowoduje natychmiastowe zg(cid:227)oszenie b(cid:227)(cid:269)du. Trzecim sposobem u(cid:304)ywania funkcji jest ich wywo(cid:227)ywanie jako konstrukto- rów. Konstruktory, podobnie jak metody i zwyk(cid:227)e funkcje, definiuje si(cid:269) za pomoc(cid:264) s(cid:227)owa kluczowego function: function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; } Je(cid:292)li wywo(cid:227)asz funkcj(cid:269) User z operatorem new, zostanie ona potraktowana jak konstruktor: var u = new User( sfalken , 0ef33ae791068ec64b502d6cb0191387 ); u.name; // sfalken Wywo(cid:227)anie konstruktora, w odró(cid:304)nieniu od wywo(cid:227)a(cid:281) funkcji i metod, powo- duje przekazanie nowego obiektu jako warto(cid:292)ci this i niejawne zwrócenie no- wego obiektu jako wyniku. G(cid:227)ównym zadaniem konstruktora jest inicjowanie obiektów. Co warto zapami(cid:269)ta(cid:254)? (cid:81) W wywo(cid:227)aniach metod nale(cid:304)y poda(cid:254) obiekt (odbiorc(cid:269)), w którym szukana b(cid:269)dzie w(cid:227)a(cid:292)ciwo(cid:292)(cid:254) w postaci tej metody. (cid:81) W wywo(cid:227)aniach funkcji odbiorc(cid:264) jest obiekt globalny (w trybie strict jest to warto(cid:292)(cid:254) undefined). Wywo(cid:227)ywanie metod jak zwyk(cid:227)ych funkcji rzadko jest przydatne. (cid:81) Konstruktory s(cid:264) wywo(cid:227)ywane za pomoc(cid:264) s(cid:227)owa kluczowego new, a ich od- biorc(cid:264) s(cid:264) nowe obiekty. Poleć książkęKup książkę 74 Rozdzia(cid:227) 3. Korzystanie z funkcji Sposób 19. Funkcje wy(cid:304)szego poziomu Funkcje wy(cid:304)szego poziomu by(cid:227)y w przesz(cid:227)o(cid:292)ci znakiem rozpoznawczym mistrzów programowania funkcyjnego. Te tajemnicze nazwy sugeruj(cid:264), (cid:304)e ma- my tu do czynienia z zaawansowan(cid:264) technik(cid:264) programistyczn(cid:264). Nic bardziej mylnego. Wykorzystanie zwi(cid:269)z(cid:227)ej elegancji funkcji cz(cid:269)sto prowadzi do powsta- nia prostszego i krótszego kodu. Przez lata w j(cid:269)zykach skryptowych wprowa- dzano techniki z tego obszaru. Dzi(cid:269)ki temu uda(cid:227)o si(cid:269) przybli(cid:304)y(cid:254) u(cid:304)ytkownikom niektóre z najlepszych idiomów z dziedziny programowania funkcyjnego. Funkcje wy(cid:304)szego poziomu to po prostu funkcje, które przyjmuj(cid:264) inne funkcje jako argumenty lub zwracaj(cid:264) inne funkcje jako wynik. Zw(cid:227)aszcza przyjmo- wanie argumentu w postaci funkcji (nazywanej cz(cid:269)sto funkcj(cid:264) wywo(cid:227)ywan(cid:264) zwrotnie, poniewa(cid:304) jest wywo(cid:227)ywana przez funkcj(cid:269) wy(cid:304)szego poziomu) to wyj(cid:264)tkowo przydatny i zwi(cid:269)z(cid:227)y idiom, cz(cid:269)sto wykorzystywany w programach w JavaScripcie. Przyjrzyj si(cid:269) standardowej metodzie sort tablic. Aby mog(cid:227)a ona dzia(cid:227)a(cid:254) dla wszystkich mo(cid:304)liwych tablic, wymaga od programu wywo(cid:227)uj(cid:264)cego okre(cid:292)lenia, jak nale(cid:304)y porównywa(cid:254) 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(cid:304)na by(cid:227)o za(cid:304)(cid:264)da(cid:254), aby program wywo(cid:227)uj(cid:264)cy przekazywa(cid:227) obiekt z metod(cid:264) compare, jednak poniewa(cid:304) wymagana jest tylko jedna metoda, bezpo(cid:292)rednie przyjmowanie funkcji to prostsze i bardziej zwi(cid:269)- z(cid:227)e rozwi(cid:264)zanie. Przedstawiony wcze(cid:292)niej przyk(cid:227)ad mo(cid:304)na upro(cid:292)ci(cid:254) jeszcze bardziej, stosuj(cid:264)c funkcj(cid:269) anonimow(cid:264): [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(cid:304)szego poziomu cz(cid:269)sto pozwala upro(cid:292)ci(cid:254) kod i wyeli- minowa(cid:254) nudny, szablonowy kod. Dla wielu typowych operacji na tablicach istniej(cid:264) (cid:292)wietne abstrakcje wy(cid:304)szego rz(cid:269)du, z którymi warto si(cid:269) zapozna(cid:254). Poleć książkęKup książkę Sposób 19. Funkcje wy(cid:304)szego poziomu 75 Przyjrzyj si(cid:269) prostemu zadaniu przekszta(cid:227)cania tablicy (cid:227)a(cid:281)cuchów znaków. Za pomoc(cid:264) p(cid:269)tli mo(cid:304)na napisa(cid:254) nast(cid:269)puj(cid:264)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(cid:269)ki wygodnej metodzie map tablic (wprowadzonej w standardzie ES5) mo(cid:304)na ca(cid:227)kowicie zrezygnowa(cid:254) z p(cid:269)tli i zaimplementowa(cid:254) transformacj(cid:269) ko- lejnych elementów za pomoc(cid:264) funkcji lokalnej: var names = [ Fred , Wilma , Pebbles ]; var upper = names.map(function(name) { return name.toUpperCase(); }); upper; // [ FRED , WILMA , PEBBLES ] Gdy przyzwyczaisz si(cid:269) do korzystania z funkcji wy(cid:304)szego poziomu, zaczniesz dostrzega(cid:254) okazje do ich samodzielnego pisania. Dobr(cid:264) oznak(cid:264) wskazuj(cid:264)c(cid:264), (cid:304)e taka funkcja mo(cid:304)e okaza(cid:254) si(cid:269) przydatna, jest wyst(cid:269)powanie powtarzaj(cid:264)ce- go si(cid:269) lub podobnego kodu. Za(cid:227)ó(cid:304)my, (cid:304)e jedna cz(cid:269)(cid:292)(cid:254) programu tworzy (cid:227)a(cid:281)- cuch znaków sk(cid:227)adaj(cid:264)cy si(cid:269) 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(cid:269)(cid:292)(cid:254) programu generuje (cid:227)a(cid:281)cuch znaków obejmuj(cid:264)cy cyfry: var digits = ; for (var i = 0; i 10; i++) { digits += i; } digits; // 0123456789 W jeszcze innym fragmencie program tworzy (cid:227)a(cid:281)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(cid:304)dy fragment generuje inny (cid:227)a(cid:281)cuch znaków, jednak logika dzia(cid:227)ania jest za ka(cid:304)dym razem podobna. Ka(cid:304)da z pokazanych p(cid:269)tli generuje (cid:227)a(cid:281)cuch zna- ków na podstawie scalania wyników oblicze(cid:281) daj(cid:264)cych poszczególne znaki. Poleć książkęKup książkę 76 Rozdzia(cid:227) 3. Korzystanie z funkcji Mo(cid:304)na wyodr(cid:269)bni(cid:254) wspólne aspekty i umie(cid:292)ci(cid:254) je w jednej funkcji narz(cid:269)- dziowej: function buildString(n, callback) { var result = ; for (var i = 0; i n; i++) { result += callback(i); } return result; } Zauwa(cid:304), (cid:304)e w funkcji buildString znajduj(cid:264) si(cid:269) wszystkie wspólne elementy ka(cid:304)dej p(cid:269)tli, natomiast dla zmiennych aspektów stosowane s(cid:264) parametry. Liczbie iteracji p(cid:269)tli odpowiada zmienna n, a do tworzenia ka(cid:304)dego fragmentu (cid:227)a(cid:281)cucha s(cid:227)u(cid:304)y funkcja callback. Teraz mo(cid:304)na upro(cid:292)ci(cid:254) ka(cid:304)dy z trzech przyk(cid:227)adów i zastosowa(cid:254) w nich funkcj(cid:269) 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(cid:304)szego poziomu przynosi wiele korzy(cid:292)ci. Je(cid:292)li w im- plementacji wyst(cid:269)puj(cid:264) skomplikowane fragmenty (trzeba na przyk(cid:227)ad w(cid:227)a(cid:292)ci- wie obs(cid:227)u(cid:304)y(cid:254) warunki graniczne dla p(cid:269)tli), znajduj(cid:264) si(cid:269) one w funkcji wy(cid:304)- szego poziomu. Dzi(cid:269)ki temu wystarczy naprawi(cid:254) b(cid:227)(cid:269)dy w logice raz, zamiast szuka(cid:254) wszystkich wyst(cid:264)pie(cid:281) danego wzorca rozrzuconych po programie. Tak(cid:304)e je(cid:292)li stwierdzisz, (cid:304)e trzeba zoptymalizowa(cid:254) wydajno(cid:292)(cid:254) operacji, wystar- czy to zrobi(cid:254) w jednym miejscu. Ponadto nadanie abstrakcji jednoznacznej nazwy (takiej jak buildString, czyli „twórz (cid:227)a(cid:281)cuch znaków”) jasno informuje czytelników kodu o jego dzia(cid:227)aniu. Dzi(cid:269)ki temu nie trzeba analizowa(cid:254) szcze- gó(cid:227)ów implementacji. Stosowanie funkcji wy(cid:304)szego poziomu po dostrze(cid:304)eniu, (cid:304)e w kodzie powtarza si(cid:269) ten sam wzorzec, prowadzi do powstawania bardziej zwi(cid:269)z(cid:227)ego kodu, zwi(cid:269)k- szenia produktywno(cid:292)ci i poprawy czytelno(cid:292)ci kodu. Zwracanie uwagi na po- wtarzaj(cid:264)ce si(cid:269) wzorce i przenoszenie ich do funkcji narz(cid:269)dziowych wy(cid:304)szego poziomu to wa(cid:304)ny nawyk, który warto sobie rozwin(cid:264)(cid:254). Poleć książkęKup książkę Sposób 20. Instrukcja call do wywo(cid:227)ywania metod dla niestandardowego odbiorcy 77 Co warto zapami(cid:269)ta(cid:254)? (cid:81) Funkcje wy(cid:304)szego poziomu charakteryzuj(cid:264) si(cid:269) tym, (cid:304)e przyjmuj(cid:264) inne funkcje jako argumenty lub zwracaj(cid:264) funkcje jako wyniki. (cid:81) Zapoznaj si(cid:269) z funkcjami wy(cid:304)szego poziomu z istniej(cid:264)cych bibliotek. (cid:81) Naucz si(cid:269) wykrywa(cid:254) powtarzaj(cid:264)ce si(cid:269) wzorce, które mo(cid:304)na zast(cid:264)pi(cid:254) funk- cjami wy(cid:304)szego poziomu. Sposób 20. Instrukcja call do wywo(cid:227)ywania metod dla niestandardowego odbiorcy Sposób 20. Stosuj instrukcj(cid:269) call do wywo(cid:227)ywania metod dla niestandardowego odbiorcy Odbiorca funkcji lub metody (czyli warto(cid:292)(cid:254) wi(cid:264)zana ze specjalnym s(cid:227)owem kluczowym this) standardowo jest okre(cid:292)lany na podstawie sk(cid:227)adni wywo(cid:227)a- nia. Wywo(cid:227)anie metody powoduje zwi(cid:264)zanie ze s(cid:227)owem kluczowym this obiektu, w którym dana metoda jest wyszukiwana. Czasem jednak trzeba wywo(cid:227)a(cid:254) funkcj(cid:269) dla niestandardowego odbiorcy, a nie jest ona jego w(cid:227)a(cid:292)ciwo- (cid:292)ci(cid:264). Mo(cid:304)na oczywi(cid:292)cie doda(cid:254) potrzebn(cid:264) metod(cid:269) jako now(cid:264) w(cid:227)a(cid:292)ciwo(cid:292)(cid:254) 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(cid:292)cie jest niewygodne, a nawet niebezpieczne. Cz(cid:269)sto nie nale- (cid:304)y, a nawet nie da si(cid:269) zmodyfikowa(cid:254) obiektu takiego jak obj w przyk(cid:227)adzie. Niezale(cid:304)nie od nazwy wybranej dla w(cid:227)a(cid:292)ciwo(cid:292)ci (tu jest to temporary) istnieje ryzyko kolizji z istniej(cid:264)c(cid:264) w(cid:227)a(cid:292)ciwo(cid:292)ci(cid:264) obiektu. Ponadto niektóre obiekty s(cid:264) zamro(cid:304)one lub zamkni(cid:269)te, co uniemo(cid:304)liwia dodawanie do nich nowych w(cid:227)a(cid:292)ciwo(cid:292)ci. Ponadto dodawanie dowolnych w(cid:227)a(cid:292)ciwo(cid:292)ci do obiektów to z(cid:227)a praktyka — zw(cid:227)aszcza gdy s(cid:264) to obiekty utworzone przez innego programist(cid:269) (zobacz Sposób 42.). Na szcz(cid:269)(cid:292)cie funkcje maj(cid:264) wbudowan(cid:264) metod(cid:269) call, umo(cid:304)liwiaj(cid:264)c(cid:264) podanie niestandardowego odbiorcy. Wywo(cid:227)anie funkcji za pomoc(cid:264) metody call: f.call(obj, arg1, arg2, arg3); dzia(cid:227)a podobnie jak wywo(cid:227)anie bezpo(cid:292)rednie: f(arg1, arg2, arg3); Ró(cid:304)nica polega na tym, (cid:304)e w metodzie call pierwszy argument to jawnie wskazany obiekt odbiorcy. Metoda call jest wygodna przy wywo(cid:227)ywaniu metod, które mog(cid:227)y zosta(cid:254) usu- ni(cid:269)te, zmodyfikowane lub zast(cid:264)pione. W sposobie 45. przedstawiony jest przy- datny przyk(cid:227)ad, ilustruj(cid:264)cy wywo(cid:227)ywanie metody hasOwnProperty dla dowolnych Poleć książkęKup książkę 78 Rozdzia(cid:227) 3. Korzystanie z funkcji obiektów (nawet dla s(cid:227)ownika). W s(cid:227)owniku sprawdzenie w(cid:227)a(cid:292)ciwo(cid:292)ci hasOw- nProperty powoduje zwrócenie warto(cid:292)ci ze s(cid:227)ownika, zamiast wywo(cid:227)ania odziedziczonej metody: dict.hasOwnProperty = 1; dict.hasOwnProperty( foo ); // Błąd: 1 nie jest funkcją Za pomoc(cid:264) metody call mo(cid:304)na wywo(cid:227)a(cid:254) metod(cid:269) hasOwnProperty dla s(cid:227)ownika, nawet je(cid:292)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(cid:304)e przy definiowaniu funkcji wy(cid:304)szego poziomu. Cz(cid:269)sto stosowany idiom dotycz(cid:264)cy funkcji wy(cid:304)szego poziomu polega na przyj- mowaniu opcjonalnych argumentów okre(cid:292)laj(cid:264)cych odbiorc(cid:269), dla którego funkcja ma zosta(cid:254) wywo(cid:227)ana. Na przyk(cid:227)ad obiekt reprezentuj(cid:264)cy tablic(cid:269) z parami klucz – warto(cid:292)(cid:254) mo(cid:304)e udost(cid:269)pnia(cid:254) metod(cid:269) 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(cid:304)liwia u(cid:304)ytkownikom obiektu podanie potrzebnej metody jako wy- wo(cid:227)ywanej zwrotnie funkcji f z metody table.forEach i wskazanie odpowied- niego odbiorcy. Dzi(cid:269)ki temu mo(cid:304)na na przyk(cid:227)ad wygodnie skopiowa(cid:254) za- warto(cid:292)(cid:254) jednej tablicy do drugiej: table1.forEach(table2.addEntry, table2); Ten kod u(cid:304)ywa metody addEntry obiektu table2 (mo(cid:304)na te(cid:304) pobra(cid:254) t(cid:269) metod(cid:269) z obiektu Table.prototype lub table1), a metoda forEach wielokrotnie wywo(cid:227)uje metod(cid:269) addEntry dla odbiorcy table2. Zauwa(cid:304), (cid:304)e cho(cid:254) metoda addEntry oczekuje tylko dwóch argumentów, metoda forEach wywo(cid:227)uje j(cid:264) z trzema argumentami: kluczem, warto(cid:292)ci(cid:264) i indeksem. Dodatkowy argument w postaci indeksu nie powoduje problemów, poniewa(cid:304) metoda addEntry po prostu go pomija. Poleć książkęKup książkę Sposób 21. Instrukcja apply do wywo(cid:227)ywania funkcji o ró(cid:304)nej liczbie argumentów 79 Co warto zapami(cid:269)ta(cid:254)? (cid:81) U(cid:304)ywaj metody call do wywo(cid:227)ywania funkcji dla niestandardowych od- biorców. (cid:81) Stosuj metod(cid:269) call do wywo(cid:227)ywania metod, które mog(cid:264) nie istnie(cid:254) w danym obiekcie. (cid:81) U(cid:304)ywaj metody call do definiowania funkcji wy(cid:304)szego poziomu, umo(cid:304)li- wiaj(cid:264)cych klientom okre(cid:292)lanie odbiorcy wywo(cid:227)ywanych zwrotnie funkcji. Sposób 21. Instrukcja apply do wywo(cid:227)ywania funkcji o ró(cid:304)nej liczbie argumentów Sposób 21. Stosuj instrukcj(cid:269) apply do wywo(cid:227)ywania funkcji o ró(cid:304)nej liczbie argumentów Wyobra(cid:302) sobie, (cid:304)e dost(cid:269)pna jest funkcja obliczaj(cid:264)ca (cid:292)redni(cid:264) z dowolnej liczby warto(cid:292)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(cid:264) wariadyczn(cid:264) (ang. variadic), inaczej funkcj(cid:264) o zmiennej arno(cid:292)ci (arno(cid:292)(cid:254) funkcji to liczba oczekiwanych przez ni(cid:264) argu- mentów). Oznacza to tyle, (cid:304)e mo(cid:304)e przyjmowa(cid:254) dowoln(cid:264) liczb(cid:269) argumentów. Wersja funkcji average maj(cid:264)ca sta(cid:227)(cid:264) arno(cid:292)(cid:254) prawdopodobnie pobiera(cid:227)aby je- den argument z tablic(cid:264) warto(cid:292)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(cid:269)z(cid:227)a i (cho(cid:254) to kwestia dyskusyjna) bar- dziej elegancka. Funkcje wariadyczne maj(cid:264) wygodn(cid:264) sk(cid:227)adni(cid:269) — przynajm- niej wtedy, gdy u(cid:304)ytkownik od razu wie, ile argumentów chce poda(cid:254). Tak dzieje si(cid:269) w przedstawionych przyk(cid:227)adach. Wyobra(cid:302) sobie jednak, (cid:304)e tablica warto(cid:292)ci jest tworzona w nast(cid:269)puj(cid:264)cy sposób: var scores = getAllScores(); Jak u(cid:304)y(cid:254) funkcji average do obliczenia (cid:292)redniej z tych warto(cid:292)ci? average(/* ? */); Na szcz(cid:269)(cid:292)cie funkcje maj(cid:264) wbudowan(cid:264) metod(cid:269) apply. Dzia(cid:227)a ona podobnie jak metoda call, ale jest zaprojektowana w okre(cid:292)lonym celu. Metoda apply przyj- muje tablic(cid:269) argumentów i wywo(cid:227)uje dan(cid:264) funkcj(cid:269) w taki sposób, jakby ka(cid:304)dy element tablicy by(cid:227) odr(cid:269)bnym argumentem wywo(cid:227)ania tej funkcji. Oprócz Poleć książkęKup książkę 80 Rozdzia(cid:227) 3. Korzystanie z funkcji tablicy argumentów metoda apply przyjmuje dodatkowo pierwszy argument, okre(cid:292)laj(cid:264)cy obiekt this dla wywo(cid:227)ywanej funkcji. Poniewa(cid:304) funkcja average nie u(cid:304)ywa obiektu this, wystarczy poda(cid:254) warto(cid:292)(cid:254) null: var scores = getAllScores(); average.apply(null, scores); Je(cid:292)li tablica scores b(cid:269)dzie mia(cid:227)a na przyk(cid:227)ad trzy elementy, przedstawiony kod zadzia(cid:227)a tak samo jak poni(cid:304)sza wersja: average(scores[0], scores[1], scores[2]); Metoda apply dzia(cid:227)a tak(cid:304)e dla metod wariadycznych. Na przyk(cid:227)ad obiekt buffer mo(cid:304)e mie(cid:254) wariadyczn(cid:264) metod(cid:269) append, przeznaczon(cid:264) do dodawania elemen- tów do jego wewn(cid:269)trznego stanu (aby zrozumie(cid:254) implementacj(cid:269) tej metody append, zapoznaj si(cid:269) ze Sposobem 22.): var buffer = { state: [], append: function() { for (var i = 0, n = arguments.length; i n; i++) { this.state.push(arguments[i]); } } }; Metod(cid:269) append mo(cid:304)na wywo(cid:227)a(cid:254) z dowoln(cid:264) liczb(cid:264) argumentów: buffer.append( Witaj, ); buffer.append(firstName, , lastName, ! ); buffer.append(newline); Za pomoc(cid:264) argumentu metody apply okre(cid:292)laj(cid:264)cego obiekt this mo(cid:304)na te(cid:304) wywo(cid:227)a(cid:254) t(cid:269) metod(cid:269) z generowan(cid:264) tablic(cid:264): buffer.append.apply(buffer, getInputStrings()); Zwró(cid:254) uwag(cid:269) na znaczenie argumentu buffer. Je(cid:292)li przeka(cid:304)esz niew(cid:227)a(cid:292)ciwy obiekt, metoda append spróbuje zmodyfikowa(cid:254) w(cid:227)a(cid:292)ciwo(cid:292)(cid:254) state nieodpowied- niego obiektu. Co warto zapami(cid:269)ta(cid:254)? (cid:81) Stosuj metod(cid:269) apply do wywo(cid:227)ywania funkcji wariadycznych z generowa- nymi tablicami argumentów. (cid:81) Za pomoc(cid:264) pierwszego argumentu metody apply mo(cid:304)esz wskaza(cid:254) odbiorc(cid:269) metod wariadycznych. Poleć książkęKup książkę Sposób 22. Stosuj s(cid:227)owo kluczowe arguments do tworzenia funkcji wariadycznych 81 Sposób 22. Stosuj s(cid:227)owo kluczowe arguments do tworzenia funkcji wariadycznych W sposobie 21. znajduje si(cid:269) opis funkcji wariadycznej average. Potrafi ona zwró- ci(cid:254) (cid:292)redni(cid:264) z dowolnej liczby argumentów. Jak samodzielnie zaimplemen- towa(cid:254) funkcj(cid:269) wariadyczn(cid:264)? Napisanie funkcji averageOfArray o sta(cid:227)ej arno(cid:292)ci jest stosunkowo (cid:227)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(cid:304)ywany jest jeden parametr formalny — zmienna a z listy parametrów. Gdy u(cid:304)ytkownicy wywo(cid:227)uj(cid:264) funkcj(cid:269) averageOf (cid:180)Array, podaj(cid:264) pojedynczy argument (nazywany tak w celu odró(cid:304)nienia od parametru formalnego). Jest nim tablica warto(cid:292)ci. Wersja wariadyczna wygl(cid:264)da niemal tak samo, ale nie s(cid:264) w niej jawnie zdefi- niowane (cid:304)adne parametry formalne. Zamiast tego wykorzystywany jest fakt, (cid:304)e JavaScript dodaje do ka(cid:304)dej funkcji niejawn(cid:264) zmienn(cid:264) lokaln(cid:264) o nazwie arguments. Obiekt arguments umo(cid:304)liwia dost(cid:269)p do argumentów w sposób przypo- minaj(cid:264)cy u(cid:304)ywanie tablicy. Ka(cid:304)dy argument ma okre(cid:292)lony indeks, a w(cid:227)a(cid:292)ci- wo(cid:292)(cid:254) length okre(cid:292)la, ile argumentów zosta(cid:227)o podanych. To sprawia, (cid:304)e w funkcji average o zmiennej arno(cid:292)ci mo(cid:304)na przej(cid:292)(cid:254) w p(cid:269)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(cid:264) du(cid:304)o swobody. W ró(cid:304)nych miejscach mo(cid:304)na je wy- wo(cid:227)ywa(cid:254) z wykorzystaniem odmiennej liczby argumentów. Jednak same w sobie maj(cid:264) pewne ograniczenia, poniewa(cid:304) gdy u(cid:304)ytkownicy chc(cid:264) je wywo- (cid:227)a(cid:254) dla generowanej tablicy argumentów, musz(cid:264) zastosowa(cid:254) opisan(cid:264) w spo- sobie 21. metod(cid:269) apply. Gdy w celu u(cid:227)atwienia pracy udost(cid:269)pniasz funkcj(cid:269) o zmiennej arno(cid:292)ci, to zgodnie z ogóln(cid:264) regu(cid:227)(cid:264) powiniene(cid:292) te(cid:304) utworzy(cid:254) wersj(cid:269) o sta(cid:227)ej arno(cid:292)ci, przyjmuj(cid:264)c(cid:264) jawnie podan(cid:264) tablic(cid:269). Zwykle jest to (cid:227)atwe, po- niewa(cid:304) przewa(cid:304)nie mo(cid:304)na zaimplementowa(cid:254) funkcj(cid:269) wariadyczn(cid:264) jako prost(cid:264) nak(cid:227)adk(cid:269) przekazuj(cid:264)c(cid:264) zadania do wersji o sta(cid:227)ej arno(cid:292)ci: Poleć książkęKup książkę 82 Rozdzia(cid:227) 3. Korzystanie z funkcji function average() { return averageOfArray(arguments); } Dzi(cid:269)ki temu u(cid:304)ytkownicy funkcji nie musz(cid:264) ucieka(cid:254) si(cid:269) do stosowania meto- dy apply, która mo(cid:304)e zmniejsza(cid:254) czytelno(cid:292)(cid:254) kodu i cz(cid:269)sto prowadzi do spadku wydajno(cid:292)ci. Co warto zapami(cid:269)ta(cid:254)? (cid:81) Stosuj niejawny obiekt arguments do implementowania funkcji o zmiennej arno(cid:292)ci. (cid:81) Pomy(cid:292)l o udost(cid:269)pnieniu obok funkcji wariadycznych dodatkowych wersji o sta(cid:227)ej arno(cid:292)ci, aby u(cid:304)ytkownicy nie musieli stosowa(cid:254) metody apply. Sposób 23. Nigdy nie modyfikuj obiektu arguments Obiekt arguments wprawdzie wygl(cid:264)da jak tablica, ale niestety nie zawsze dzia(cid:227)a w ten sposób. Programi(cid:292)ci znaj(cid:264)cy Perla i uniksowe skrypty pow(cid:227)oki s(cid:264) przyzwyczajeni do stosowania techniki przesuwania elementów w kierunku pocz(cid:264)tku tablicy argumentów. Tablice w JavaScripcie udost(cid:269)pniaj(cid:264) metod(cid:269) shift, która usuwa pierwszy element tablicy i przesuwa wszystkie kolejne elementy o jedn(cid:264) pozycj(cid:269). Jednak obiekt arguments nie jest egzemplarzem standardowego typu Array, dlatego nie mo(cid:304)na bezpo(cid:292)rednio wywo(cid:227)a(cid:254) metody arguments.shift(). Mo(cid:304)e si(cid:269) wydawa(cid:254), (cid:304)e dzi(cid:269)ki metodzie call da si(cid:269) pobra(cid:254) metod(cid:269) shift tablic i wywo(cid:227)a(cid:254) j(cid:264) dla obiektu arguments. Na pozór jest to sensowny sposób imple- mentacji funkcji takiej jak callMethod, która przyjmuje obiekt i nazw(cid:269) metody oraz próbuje wywo(cid:227)a(cid:254) wskazan(cid:264) metod(cid:269) tego obiektu dla wszystkich pozo- sta(cid:227)ych argumentów: function callMethod(obj, method) { var shift = [].shift; shift.call(arguments); shift.call(arguments); return obj[method].apply(obj, arguments); } Jednak dzia(cid:227)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 Poleć książkęKup książkę Sposób 23. Nigdy nie modyfikuj obiektu arguments 83 Przyczyn(cid:264) problemów z tym kodem jest to, (cid:304)e obiekt arguments nie jest kopi(cid:264) argumentów funkcji. Wszystkie nazwane argumenty to aliasy odpowiednich indeksów z obiektu arguments. Tak wi(cid:269)c obj to alias dla arguments[0], a method to alias dla arguments[1]. Jest tak nawet po usuni(cid:269)ciu elementów z obiektu arguments za pomoc(cid:264) wywo(cid:227)ania shift. To oznacza, (cid:304)e cho(cid:254) na pozór u(cid:304)ywane jest wywo(cid:227)anie obj[ add ], w rzeczywisto(cid:292)ci wywo(cid:227)anie to 17[25]. Na tym etapie zaczynaj(cid:264) si(cid:269) k(cid:227)opoty. Z powodu obowi(cid:264)zuj(cid:264)cej w JavaScripcie automatycz- nej konwersji typów warto(cid:292)(cid:254) 17 jest przekszta(cid:227)cana w obiekt typu Number, po czym pobierana jest jego (nieistniej(cid:264)ca) w(cid:227)a(cid:292)ciwo(cid:292)(cid:254) 25 , dlatego zwrócona zostaje warto(cid:292)(cid:254) undefined. Potem nast(cid:269)puje nieudana próba pobrania w(cid:227)a- (cid:292)ciwo(cid:292)ci apply obiektu undefined w celu wywo(cid:227)ania jej jako metody. Wniosek z tego jest taki, (cid:304)e relacja mi(cid:269)dzy obiektem arguments a nazwanymi pa- rametrami funkcji (cid:227)atwo staje si(cid:269) (cid:302)ród(cid:227)em problemów. Modyfikacja obiektu arguments grozi przekszta(cid:227)ceniem nazwanych parametrów funkcji w bezsen- sowne dane. W trybie strict ze standardu ES5 komplikacje s(cid:264) jeszcze wi(cid:269)ksze. Parametry funkcji w tym trybie nie s(cid:264) aliasami elementów obiektu arguments. Aby zademonstrowa(cid:254) ró(cid:304)nic(cid:269), mo(cid:304)na napisa(cid:254) funkcj(cid:269) aktualizuj(cid:264)c(cid:264) 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(cid:304)o bezpieczniej jest nigdy nie modyfikowa(cid:254) obiektu arguments. Mo(cid:304)- na (cid:227)atwo uzyska(cid:254) ten efekt, kopiuj(cid:264)c najpierw elementy z tego obiektu do zwyk(cid:227)ej tablicy. Oto prosty idiom ilustruj(cid:264)cy tak(cid:264) modyfikacj(cid:269): var args = [].slice.call(arguments); Metoda slice tablic tworzy kopi(cid:269) tablicy, gdy zostanie wywo(cid:227)ana bez dodatko- wych argumentów. Zwracany wynik to egzemplarz standardowego typu Array. Ten egzemplarz nie jest aliasem (cid:304)adnych obiektów i umo(cid:304)liwia bezpo(cid:292)redni dost(cid:269)p do wszystkich metod typu Array. Aby naprawi(cid:254) implementacj(cid:269) metody callMethod, nale(cid:304)y skopiowa(cid:254) zawarto(cid:292)(cid:254) obiektu arguments. Poniewa(cid:304) potrzebne s(cid:264) tylko elementy po obj i method, do metody slice mo(cid:304)na przekaza(cid:254) pocz(cid:264)tkowy indeks równy 2: function callMethod(obj, method) { var args = [].slice.call(arguments, 2); return obj[method].apply(obj, args); } Poleć książkęKup książkę 84 Rozdzia(cid:227) 3. Korzystanie z funkcji Teraz metoda callMethod dzia(cid:227)a zgodnie z oczekiwaniami: var obj = { add: function(x, y) { return x + y; } }; callMethod(obj, add , 17, 25); // 42 Co warto zapami(cid:269)ta(cid:254)? (cid:81) Nigdy nie modyfikuj obiektu arguments. (cid:81) Je(cid:292)li chcesz zmodyfikowa(cid:254) zawarto(cid:292)(cid:254) obiektu arguments, najpierw skopiuj j(cid:264) do zwyk(cid:227)ej tablicy za pomoc(cid:264) instrukcji [].slice.call(arguments). Sposób 24. U(cid:304)ywaj zmiennych do zapisywania referencji do obiektu arguments Iterator to obiekt, który zapewnia sekwencyjny dost(cid:269)p do kolekcji danych. Typowy interfejs API udost(cid:269)pnia metod(cid:269) next, która zwraca nast(cid:269)pn(cid:264) warto(cid:292)(cid:254) z sekwencji. Za(cid:227)ó(cid:304)my, (cid:304)e chcesz napisa(cid:254) u(cid:227)atwiaj(cid:264)c(cid:264) prac(cid:269) funkcj(cid:269), która przyjmuje dowoln(cid:264) liczb(cid:269) argumentów i tworzy iterator do poruszania si(cid:269) po tych warto(cid:292)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(cid:254) dowoln(cid:264) liczb(cid:269) argumentów, dlatego obiekt iteratora nale(cid:304)y utworzy(cid:254) tak, aby przechodzi(cid:227) 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(cid:227)owy. Staje si(cid:269) to oczywiste przy próbie u(cid:304)ycia iteratora: var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6); it.next(); // undefined Poleć książkęKup książkę Sposób 25. Instrukcja bind do pobierania metod o sta(cid:227)ym odbiorcy 85 it.next(); // undefined it.next(); // undefined Problem wynika z tego, (cid:304)e nowa zmienna arguments jest niejawnie wi(cid:264)zana w ciele ka(cid:304)dej funkcji. Obiekt, który jest tu potrzebny, jest zwi(cid:264)zany z funk- cj(cid:264) values. Ale metoda next iteratora zawiera w(cid:227)asn(cid:264) zmienn(cid:264) arguments. Dlate- go gdy zwracana jest warto(cid:292)(cid:254) arguments[i++], pobierany jest argument z wy- wo(cid:227)ania it.next, a nie jeden z argumentów funkcji values. Rozwi(cid:264)zanie tego problemu jest proste — wystarczy zwi(cid:264)za(cid:254) now(cid:264) zmienn(cid:264) lokaln(cid:264) w zasi(cid:269)gu potrzebnego obiektu arguments i zadba(cid:254) o to, aby funkcje za- gnie(cid:304)d(cid:304)one u(cid:304)ywa(cid:227)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(cid:269)ta(cid:254)? (cid:81) Zwracaj uwag(cid:269) na poziom zagnie(cid:304)d(cid:304)enia funkcji, gdy u(cid:304)ywasz obiektu arguments. (cid:81) Zwi(cid:264)(cid:304) z obiektem arguments referencj(cid:269) o jawnie okre(cid:292)lonym zasi(cid:269)gu, aby móc u(cid:304)ywa(cid:254) jej w funkcjach zagnie(cid:304)d(cid:304)onych. Sposób 25. Instrukcja bind do pobierania metod o sta(cid:227)ym odbiorcy Sposób 25. U(cid:304)ywaj instrukcji bind do pobierania metod o sta(cid:227)ym odbiorcy Poniewa(cid:304) nie istnieje ró(cid:304)nica mi(cid:269)dzy metod(cid:264) a w(cid:227)a(cid:292)ciwo(cid:292)ci(cid:264), której warto(cid:292)ci(cid:264) jest funkcja, (cid:227)atwo mo(cid:304)na pobra(cid:254) metod(cid:269) obiektu i przekaza(cid:254) j(cid:264) bezpo(cid:292)rednio jako wywo(cid:227)anie zwrotne do funkcji wy(cid:304)szego poziomu. Nie zapominaj jednak, (cid:304)e odbiorca pobranej funkcji nie jest na sta(cid:227)e ustawiony jako obiekt, z którego t(cid:269) funkcj(cid:269) pobrano. Wyobra(cid:302) sobie prosty obiekt bufora (cid:227)a(cid:281)cuchów znaków, który zapisuje (cid:227)a(cid:281)cuchy w tablicy, co umo(cid:304)liwia ich pó(cid:302)niejsze scalenie: Poleć książkęKup książkę 86 Rozdzia(cid:227) 3. Korzystanie z funkcji var buffer = { entries: [], add: function(s) { this.entries.push(s); }, concat: function() { return this.entries.join( ); } }; Wydaje si(cid:269), (cid:304)e w celu skopiowania tablicy (cid:227)a(cid:281)cuchów znaków do bufora mo(cid:304)- na pobra(cid:254) jego metod(cid:269) add i wielokrotnie wywo(cid:227)a(cid:254) j(cid:264) dla ka(cid:304)dego elementu (cid:302)ród(cid:227)owej tablicy, u(cid:304)ywaj(cid:264)c metody forEach ze standardu ES5: var source = [ 867 , - , 5309 ]; source.forEach(buffer.add); // Błąd: elementy są niezdefiniowane Jednak odbiorc(cid:264) wywo(cid:227)ania buffer.add nie jest obiekt buffer. Odbiorca funkcji zale(cid:304)y od sposobu jej wywo(cid:227)ania, a nie jest ona wywo(cid:227)ywana bezpo(cid:292)rednio w tym miejscu. Zamiast tego zostaje ona przekazana do metody forEach, której implementacja wywo(cid:227)uje funkcj(cid:269) w niedost(cid:269)pnym dla programisty miejscu. Okazuje si(cid:269), (cid:304)e implementacja metody forEach jako domy(cid:292)lnego odbiorcy u(cid:304)y- wa obiektu globalnego. Poniewa(cid:304) obiekt globalny nie ma w(cid:227)a(cid:292)ciwo(cid:292)ci entries, przedstawiony kod zg(cid:227)asza b(cid:227)(cid:264)d. Na szcz(cid:269)(cid:292)cie metoda forEach umo(cid:304)liwia poda- nie opcjonalnego argumentu, okre(cid:292)laj(cid:264)cego odbiorc(cid:269) wywo(cid:227)ania zwrotnego. Dlatego mo(cid:304)na (cid:227)atwo rozwi(cid:264)za(cid:254) problem: var source = [ 867 , - , 5309 ]; source.forEach(buffer.add, buffer); buffer.join(); // 867-5309 Nie wszystkie funkcje wy(cid:304)szego poziomu umo(cid:304)liwiaj(cid:264) u(cid:304)ytkownikom okre(cid:292)le- nie odbiorcy wywo(cid:227)a(cid:281) zwrotnych. Jak rozwi(cid:264)za(cid:254) problem, gdyby metoda forEach nie przyjmowa(cid:227)a dodatkowego argumentu okre(cid:292)laj(cid:264)cego odbiorc(cid:269)? Dobrym rozwi(cid:264)zaniem jest utworzenie funkcji lokalnej, która wywo(cid:227)uje metod(cid:269) buffer.add przy u(cid:304)yciu odpowiedniej sk(cid:227)adni: var source = [ 867 , - , 5309 ]; source.forEach(function(s) { buffer.add(s); }); buffer.join(); // 867-5309 W tej wersji u(cid:304)ywana jest funkcja nak(cid:227)adkowa, która bezpo(cid:292)rednio wywo(cid:227)uje add jako metod(cid:269) obiektu buffer. Zauwa(cid:304), (cid:304)e sama funkcja nak(cid:227)adkowa w ogóle nie u(cid:304)ywa s(cid:227)owa kluczowego this. Niezale(cid:304)nie od sposobu wywo(cid:227)ania tej funkcji nak(cid:227)adkowej (mo(cid:304)na j(cid:264) wywo(cid:227)a(cid:254) jak zwyk(cid:227)(cid:264) funkcj(cid:269), jak metod(cid:269) innego obiektu lub przy u(cid:304)yciu instrukcji call) zawsze przekazuje ona argument do docelowej tablicy. Wersja funkcji wi(cid:264)(cid:304)(cid:264)ca odbiorc(cid:269) z konkretnym obiektem jest tworzona tak cz(cid:269)- sto, (cid:304)e w standardzie ES5 dodano obs(cid:227)ug(cid:269) tego wzorca w bibliotece. Obiekty Poleć książkęKup książkę Sposób 26. U(cid:304)ywaj metody bind do wi(cid:264)zania funkcji z podzbiorem argumentów 87 reprezentuj(cid:264)ce funkcje maj(cid:264) metod(cid:269) bind, która przyjmuje obiekt odbiorcy i generuje funkcj(cid:269) nak(cid:227)adkow(cid:264) wywo(cid:227)uj(cid:264)c(cid:264) pierwotn(cid:264) funkcj(cid:269) jako metod(cid:269) odbiorcy. Za pomoc(cid:264) metody bind mo(cid:304)na upro(cid:292)ci(cid:254) przyk(cid:227)adowy kod: var source = [ 867 , - , 5309 ]; source.forEach(buffer.add.bind(buffer)); buffer.join(); // 867-5309 Pami(cid:269)taj, (cid:304)e instrukcja buffer.add.bind(buffer) tworzy now(cid:264) funkcj(cid:269), zamiast modyfikowa(cid:254) funkcj(cid:269) buffer.add. Nowa funkcja dzia(cid:227)a tak samo jak pierwotna, ale jej odbiorca to obiekt buffer. Pierwotna funkcja pozostaje niezmieniona. Oznacza to, (cid:304)e: buffer.add === buffer.add.bind(buffer); // false Jest to subtelna, ale wa(cid:304)na ró(cid:304)nica. Oznacza to, (cid:304)e metod(cid:269) bind mo(cid:304)na bez- piecznie wywo(cid:227)a(cid:254) nawet dla funkcji u(cid:304)ywanych w innych miejscach pro- gramu. Ma to znaczenie zw(cid:227)aszcza w przypadku wspó(cid:227)u(cid:304)ytkowanych metod z prototypów. Taka metoda b(cid:269)dzie dzia(cid:227)a(cid:254) prawid(cid:227)owo tak(cid:304)e dla obiektów po- tomnych prototypu. Wi(cid:269)cej informacji o obiektach i prototypach znajdziesz w rozdziale 4. Co warto zapami(cid:269)ta(cid:254)? (cid:81) Pami(cid:269)taj, (cid:304)e pobranie metody nie prowadzi do ustawienia odbiorcy metody na obiekt, z którego ona pochodzi. (cid:81) Przy przekazywaniu metody obiektu do funkcji wy(cid:304)szego poziomu wykorzy- staj funkcj(cid:269) anonimow(cid:264), aby wywo(cid:227)a(cid:254) metod(cid:269) dla odpowiedniego odbiorcy. (cid:81) Stosuj metod(cid:269) bind do szybkiego tworzenia funkcji zwi(cid:264)zanej z odpowiednim odbiorc(cid:264). Sposób 26. U(cid:304)ywaj metody bind do wi(cid:264)zania funkcji z podzbiorem argumentów Sposób 26. U(cid:304)ywaj metody bind do wi(cid:264)zania funkcji z podzbiorem argumentów (technika currying) Metoda bind funkcji przydaje si(cid:269) nie tylko do wi(cid:264)zania metod z odbiorcami. Wyobra(cid:302) sobie prost(cid:264) funkcj(cid:269) tworz(cid:264)c(cid:264) adresy URL na podstawie ich cz(cid:269)(cid:292)ci sk(cid:227)adowych. function simpleURL(protocol, domain, path) { return protocol + :// + domain + / + path; } W programie potrzebne mo(cid:304)e by(cid:254) generowanie bezwzgl(cid:269)dnych adresów URL na podstawie (cid:292)cie(cid:304)ek specyficznych dla witryny. Naturalnym sposobem na wykonanie tego zadania jest u(cid:304)ycie metody map ze standardu ES5. Poleć książkęKup książkę 88 Rozdzia(cid:227) 3. Korzystanie z funkcji var urls = paths.map(function(path) { return simpleURL( http , siteDomain, path); }); Zauwa(cid:304), (cid:304)e w tej anonimowej funkcji w ka(cid:304)dym powtórzeniu operacji przez metod(cid:269) map u(cid:304)ywane s(cid:264) ten sam (cid:227)a(cid:281)cuch znaków z protoko(cid:227)em i ten sam (cid:227)a(cid:281)- cuch znaków z domen(cid:264) witryny. Dwa pierwsze argumenty funkcji simpleURL s(cid:264) niezmienne w ka(cid:304)dej iteracji. Potrzebny jest tylko trzeci argument. Mo(cid:304)na wykorzysta(cid:254) metod(cid:269) bind funkcji simpleURL, aby automatycznie uzyska(cid:254) po- trzebn(cid:264) funkcj(cid:269): var urls = paths.map(simpleURL.bind(null, http , siteDomain)); Wywo(cid:227)anie simpleURL.bind tworzy now(cid:264) funkcj(cid:269), która deleguje zadania do funkcji simpleURL. Jak zawsze w pierwszym argumencie metody bind podawany jest od- biorca. Poniewa(cid:304) funkcja simpleURL go nie potrzebuje, mo(cid:304)na poda(cid:254) tu dowoln(cid:264) warto(cid:292)(cid:254) (standardowo u(cid:304)ywane s(cid:264) warto(cid:292)ci null i undefined). Argumenty przeka- zywane do funkcji simpleURL to wynik po(cid:227)(cid:264)czenia pozosta(cid:227)ych argumentów funkcji simpleURL.bind z argumentami przekazanymi do nowej funkcji. Ozna- cza to, (cid:304)e gdy funkcja utworzona za pomoc(cid:264) instrukcji simpleURL.bind jest wywo(cid:227)ywana z jednym argumentem path, w wyniku oddelegowania zadania wywo(cid:227)anie wygl(cid:264)da tak: simpleURL( http , siteDomain, path). Technika wi(cid:264)zania funkcji z podzbiorem argumentów to currying (nazwa pocho- dzi od logika Haskella Curry’ego, który spopularyzowa(cid:227) t(cid:269) metod(cid:269) w matematy- ce). Currying pozwala na zwi(cid:269)z(cid:227)e implementowanie delegowania i nie wymaga tak du(cid:304)o szablonowego kodu jak jawne tworzenie funkcji nak(cid:227)adkowych. Co warto zapami(cid:269)ta(cid:254)? (cid:81) Za pomoc(cid:264) metody bind mo(cid:304)na utworzy(cid:254) funkcj(cid:269) deleguj(cid:264)c(cid:264) zadania, prze- kazuj(cid:264)c(cid:264) sta(cid:227)y podzbiór wymaganych argumentów. Ta technika to currying. (cid:81) Aby za pomoc(cid:264) tej techniki utworzy(cid:254) funkcj(cid:269), która ignoruje odbiorc(cid:269), jako reprezentuj(cid:264)cy go argument podaj warto(cid:292)(cid:254) null lub undefined. Sposób 27. Wybieraj domkni(cid:269)cia zamiast (cid:227)a(cid:281)cuchów znaków do hermetyzowania kodu Funkcje to wygodny sposób przechowywania kodu w postaci struktur danych i pó(cid:302)niejszego uruchamiania go. Pozwala to na stosowanie zwi(cid:269)z(cid:227)ych abstrak- cyjnych instrukcji wy(cid:304)szego poziomu, takich jak map i forEach, oraz jest istot(cid:264) asynchronicznego wykonywania operacji wej(cid:292)cia-wyj(cid:292)cia w JavaScripcie (zo- bacz rozdzia(cid:227) 7.). Jednocze(cid:292)nie mo(cid:304)na te(cid:304) zapisa(cid:254) kod jako (cid:227)a(cid:281)cuch znaków i przekazywa(cid:254) go do instrukcji eval. Programi(cid:292)ci stoj(cid:264) wi(cid:269)c przed wyborem, czy zapisa(cid:254) kod jako funkcj(cid:269), czy jako (cid:227)a(cid:281)cuch znaków. Poleć książkęKup książkę Sposób 27. Wybieraj domkni(cid:269)cia zamiast (cid:227)a(cid:281)cuchów znaków do hermetyzowania kodu 89 Gdy masz w(cid:264)tpliwo(cid:292)ci, stosuj funkcje. (cid:226)a(cid:281)cuchy znaków zapewniaj(cid:264) znacznie mniejsz(cid:264) swobod(cid:269). Wynika to z wa(cid:304)nego powodu — nie s(cid:264) domkni(cid:269)ciami. Przyjrzyj si(cid:269) prostej funkcji wielokrotnie powtarzaj(cid:264)cej okre(cid:292)lon(cid:264) przez u(cid:304)yt- kownika operacj(cid:269): function repeat(n, action) { for (var i = 0; i n; i++) { eval(action); } } W zasi(cid:269)gu globalnym ta funkcja dzia(cid:227)a poprawnie, poniewa(cid:304) wszystkie refe- rencje do zmiennych wyst(cid:269)puj(cid:264)ce w (cid:227)a(cid:281)cuchu znaków s(cid:264) interpretowane przez instrukcj(cid:269) eval jako zmienne globalne. Na przyk(cid:227)ad w skrypcie, który mierzy szybko(cid:292)(cid:254) dzia(cid:227)ania funkcji, mo(cid:304)na wykorzysta(cid:254) 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(cid:269)d(cid:264) ju(cid:304) 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, (cid:304)e instrukcja repeat wykorzystuje referencje do zmien- nych globalnych start i end. W najlepszym przypadku jedna z tych zmien- nych nie b(cid:269)dzie istnie(cid:254), a wywo(cid:227)anie funkcji benchmark doprowadzi do b(cid:227)(cid:269)du ReferenceError. Je(cid:292)li programista b(cid:269)dzie mia(cid:227) pecha, kod wywo(cid:227)a instrukcj(cid:269) push dla globalnych obiektów zwi(cid:264)zanych z nazwami start i end, a program b(cid:269)dzie dzia(cid:227)a(cid:227) nieprzewidywalnie. Bardziej odporny na b(cid:227)(cid:269)dy interfejs API przyjmuje funkcj(cid:269) zamiast (cid:227)a(cid:281)cucha znaków: function repeat(n, action) { for (var i = 0; i n; i++) { action(); } } Poleć książkęKup książkę 90 Rozdzia(cid:227) 3. Korzystanie z funkcji Dzi(cid:269)ki temu w skrypcie benchmark mo(cid:304)na bezpiecznie u(cid:304)ywa(cid:254) zmiennych lo- kalnych z domkni(cid:269)cia przekazywanego jako wielokrotnie uruchamiane wy- wo(cid:227)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(cid:264) eval polega na tym, (cid:304)e silniki o wysokiej wydajno(cid:292)ci maj(cid:264) zwykle wi(cid:269)ksze problemy z optymalizacj(cid:264) kodu z (cid:227)a(cid:281)cuchów znaków, poniewa(cid:304) kod (cid:302)ród(cid:227)owy mo(cid:304)e nie by(cid:254) dost(cid:269)pny dla kompilatora na tyle wcze- (cid:292)nie, by mo(cid:304)na by(cid:227)o w odpowiednim momencie przeprowadzi(cid:254) optymalizacj(cid:269). Wyra(cid:304)enia funkcyjne mo(cid:304)na kompilowa(cid:254) jednocze(cid:292)nie z kodem, w którym wy- st(cid:269)puj(cid:264). Dlatego znacznie (cid:227)atwiej si(cid:269) je kompiluje w standardowy sposób. Co warto zapami(cid:269)ta(cid:254)? (cid:81) Nigdy nie stosuj lokalnych referencji w (cid:227)a(cid:281)cuchach znaków przekazywanych do interfejsu API, który wykonuje kod z (cid:227)a(cid:281)cucha za pomoc(cid:264) instrukcji eval. (cid:81) Preferuj interfejsy API, które przyjmuj(cid:264) wywo(cid:227)ywane funkcje zamiast (cid:227)a(cid:281)cu- chów znaków przekazywanych do instrukcji eval. Sposób 28. Unikaj stosowania metody toString funkcji Funkcje w JavaScripcie maj(cid:264) niezwyk(cid:227)(cid:264) cech(cid:269) — umo(cid:304)liwiaj(cid:264) wy(cid:292)wietlenie swojego kodu (cid:302)ród(cid:227)owego jako (cid:227)a(cid:281)cucha znaków: (function(x) { returnx + 1; }).toString(); // function (x) {\n return x + 1;\n} Wy(cid:292)wietlanie kodu (cid:302)ród(cid:227)owego funkcji za pomoc(cid:264) mechanizmu refleksji daje du(cid:304)o mo(cid:304)liwo(cid:292)ci, a pomys(cid:227)owi hakerzy potrafi(cid:264) znale(cid:302)(cid:254) ciekawe sposoby ich wykorzystania. Jednak metoda toString funkcji ma powa(cid:304)ne ograniczenia. Przede wszystkim standard ECMAScript nie okre(cid:292)la (cid:304)adnych wymaga(cid:281) wobec (cid:227)a(cid:281)cuchów znaków zwracanych przez metod(cid:269) toString funkcji. To oznacza, (cid:304)e ró(cid:304)ne silniki JavaScriptu mog(cid:264) zwraca(cid:254) odmienne (cid:227)a(cid:281)cuchy znaków. Mo(cid:304)liwe nawet, (cid:304)e zwrócony tekst w rzeczywisto(cid:292)ci nie b(cid:269)dzie podobny do kodu danej funkcji. Poleć książkęKup książkę Sposób 28. Unikaj s
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Efektywny JavaScript. 68 sposobów wykorzystania potęgi języka
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


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