Stefanow S. - Javascript dla programistów PHP

154 Pages • 37,451 Words • PDF • 8.5 MB
Uploaded at 2021-09-24 18:12

This document was submitted by our user and they confirm that they have the consent to share it. Assuming that you are writer or own the copyright of this document, report to us by using this DMCA report button.


B ły s k a iv ic z n ie o p c i n u j n o w y ję z y k p r o g r a m o iv a n ia !

dla programistów PHP

HELION

O ’R E IL L Y '

W ięcej na: www.ebookgigs.eu

S to y a n S te fa n o v

Spis treści Podziękowania.................................................................................................... 11 W s tę p ....................................................................................................................13 1.... W prowadzenie........................................................................................15 Zakres niniejszej książki

17

Język

18

Nauka środowiska

18

Przeglądarki

19

JavaScriptCore

20

Node.js i Rhino

21

Dłuższe przykłady

22

Zaczynamy

22

2. Składnia języka JavaScript................................................................... 23 Zmienne

23

Jakie jest zadanie znaku $ w JavaScript? Wartości

24 25

Introspekcja typeof Wartości nuli i undefined Tablice Tablice asocjacyjne Instrukcje warunkowe Operator trójargumentowy

26 27 28 29 30 30

Ścisła kontrola typu

31

Konstrukcja switch

33

Konstrukcja try-catch Pętle while i for

33 35

Pętle for-in

36

W ięcej na: www.ebookgigs.eu

Inne operatory

37

Operator in

37

Łączenie fragmentów tekstów

38

Rzutowanie typów

39

Operator void

39

Operator przecinka

40

3. Funkcje...................................................................................................... 41 Parametry domyślne

41

Dowolna liczba argumentów

42

Sztuczka z arguments .length

43

Zwracanie w yniku funkcji

44

Funkcje są obiektami

44

Nieco inna składnia

45

Zakres w idoczności zmiennych Przenoszenie na początek

46 48

Przenoszenie na początek funkcji Domknięcia Domknięcia w języku PHP Łańcuch zakresu w idoczności zmiennych

49 50 50 52

Łańcuch zakresów w konsoli W ebKit Zachowanie zakresu

54 57

Przechowywane są referencje, a nie w artości

58

Domknięcia w pętli

59

Ćwiczenie — pętla z onclick Funkcje natychmiastowe

61 62

Inicjalizacja

64

Prywatność

64

Przekazywanie i zwracanie funkcji

65

W ywołania zwrotne nie są tek stam i................................................... 67

4. Programowanie obiektow e................................................................. 69 Konstruktory i klasy

I

69

Zwracanie obiektów Dodatkowe informacje na temat this

70 72

Wymuszenie wywołania konstruktora

73

JavaScript dla programistów PHP Więcej na: www.ebookgigs.eu

Prototypy

74

Literał obiektu

75

Dostęp do w łaściwości

75

M yłące ] va r h ■ T f lt a j- ;

undefined

Rysunek 1.4. Konsola node.js Rhino to interpreter JavaScript napisany w języku Java przez program i­ stów z fundacji Mozilla. Po pobraniu pliku rhino.jar i um ieszczeniu go w odpowiedniej lokalizacji (w tym przypadku na dysku E:\) konsolę (patrz rysunek 1.5) można uruchom ić poleceniem: > Java -jar rhino.jar

Rysunek 1.5. Konsola Rhino

Nauka środowiska W ięcej na: www.ebookgigs.eu

21

Jeśli masz wybór, stosuj interpreter aplikaqi Node.js. Bardzo szybko zauwa­ żysz, że dostępne rozwiązanie REPL (pętla faz wpisyw ania, kompilacji, wykonania i wyświetlenia wyniku) jest bardzo podobne w działaniu do php -a (jeśli nie lepsze) i znacząco ułatwia wykonywanie eksperymentów oraz naukę języka.

Dłuższe przykłady Korzystając z JavaScriptCore, Node.js lub Rhino, bardzo łatwo w ykony­ w ać dłuższe przykłady. W ystarczy zapisać je w osobnycłi plikacłi. D o­ kładnie w ten sam sposób tworzy się w języku JavaScript skrypty powłoki. Zewnętrzne pliki urucliamia się, przekazując je jako parametr: $ jsc test.js $ node test.js $ Java -jar rhino.jar test.js

Zaczynamy Uzbrojeni w konsolę JavaScript możemy w yruszać na podbój języka. Roz­ dział 2. skupi się przede wszystkim na składni, podkreślając podobieństwa i różnice względem języka PHP.

www.ebookgigs.eu

22

I

Rozdział 1. W prowadzenie W ięcej na: www.ebookgigs.eu

ROZDZIAŁ 2.

Składnia języka JavaScript

JavaScript, podobnie jak język PHP, używa składni wywodzącej się z języka C, więc wszystko pow inno w yglądać znajomo. Ten rozdział skupia się na podstawowychi aspektachi, prezentując podobieństwa i różnice między zmiennymi, tabłicami, pętłam i, warunkam i i różnymi operatorami (czasem naw et dosyć dziw nym i).

Zmienne Definicja zmiennej w języku PIriP w ygłąda następująco: //P H P $n = 1;

Jej równoważnik w języku JavaScript ma postać: //Jav aS crip t var n = 1;

Nie stosuje się znaku dolara, a jedynie samą nazwę zmiennej. Podobnie jak w PHP nie określa się typu zmiennej, ponieważ jest on tak naprawdę wska­ zywany przez przypisyw aną wartość. Dla w szystkich typów danycłi sto­ suje się to samo słowo kluczowe — var. Jeśli potrzebny jest typ liczbowy, do zmiennej przypisuje się liczbę. Dokład­ nie ta sama zasada obowiązuje dla w artości logicznych i tekstów: var n = 1; //lic z b a var b = true; //M'artość logiczna var s = "witaj"; //te k st

Istnieje możliwość zadeklarowania zmiennej bez jej inicjalizacji wartością. W tej sytuacji zmienna otrzymuje specjalną w artość undefined:

Zmienne W ięcej na: www.ebookgigs.eu

|

23

var a; a ; // 'a' zaw iera sp ecjaln ą w artość undefined

Ponowna deklaracja istniejącej już zmiennej nie zmienia jej wartości z powrotem na undef ined:

4

var a = 1; var a; // 'a' n ad al zaM’iera w artość 1

Jednym poleceniem var można zadeklarować (i opcjonalnie zainicjować wartością) kilka zmiennych. Trzeba je jednak oddzielić znakiem przecinka, a na końcu umieścić średnik: var pi = 3.14, yeps = true, nopes, hi = "witaj", wrrrld = "świat";

S .7.

___ t

Z technicznego punktu widzenia var jest elementem opcjonalnym. Jeśli jednak zmienna nie została zadeklarowana wyżej w hierarchii zakresów (więcej informacji na ten temat w rozdziale 3.), pominięcie polecenia var spowoduje utworzenie zmiennej glo­ balnej. Z pewnością jako programista przekonałeś się już bole­ śnie, że zanieczyszczanie przestrzeni nazw zmiennymi global­ nymi to nic dobrego. Istnieją również pewne subtelne różnice, jeśli zmienna globalna jest deklarowana z var lub bez niego. Podsumowując, oprzyj się lenistwu i zawsze używaj polecenia var do deklarowania zmiennych.

Nazwy zmiennych mogą składać się z liter, cyfr, znaku podkreślenia i znaku dolara. Nie można jednak rozpocząć nazw y od cyfry: var var var var

_lv; vl; v_l; lv;

//p o p ra w n e //p o p ra w n e //p o p ra w n e //n iep op raw n e

>t zadanie znj

lavaScript?

To podchwytliwe pytanie zadawane czasem na rozmowach kwalifikacyjnych. Krótka odpowiedź brzmi — żadne. Znak dolara może pojawić się w dowolnym miejscu nazwy zmiennej. W cze­ śniejsze edycje języka ECM A Script zalecały, by znaku tego używ ać tylko w automatycznie generowanym kodzie, ale zalecenie to było przez progra­ mistów bardzo często ignorowane. W rzeczywistości bardzo często znaku $ 24

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

używano jako nazwy funkqi wybierającej konkretny węzeł DOM dokumentu HTML. Przypuśćmy, że mamy do cz)nruenia z następującym fragmentem kodu HTML:

Aby pobrać referenqę do elementu DOM, w wielu sytuaqach stosuje się me­ todę DOM o nazwie get El ementById(): var mydiv = document.getElementByld("mojeid");

Niestety, to naprawdę spora liczba znaków do wpisania dla niezwykle popu­ larnej operacji w przypadku pisania kodu wykonywanego po stronie klienta. Z tego pow odu w wielu bibliotekach zdefiniowano funkcję pomocniczą: function $(id) { return document.getElementByld(id);

} Dzięki temu znacząco zmniejsza się liczba znaków w kodzie: $("myid").innerHTML = "Witaj, świecie";

Sztuczka z pytaniem dotyczącym znaku $ pozwala pytającem u sprawdzić, czy kandydat ma jakiekolw iek doświadczenie z językiem JavaScript poza stosowaniem bibliotek wykorzystujących znak $. Odpowiedź typu „znak $ pozwala pobrać element D O M " jest prawdziwa w przypadku wielu bi­ bliotek i konsol przeglądarek internetowych, ale nie ma żadnego zdefinio­ wanego działania w samym języku JavaScript.

„ ^

Wiele konsol przeglądarek internetowych (między innymi dodatek Firebug) definiuje funkcję $ () ułatwiającą dostęp do elementów DOM. Istnieje również funkcja $ $ (), która tak napraw dę sta­ nowi kopię funkcji document.querySel ectorAl 1 () i umożliwia znajdowanie elementów DOM przy użyciu selektora CSS. Oto przykład: //L ista M'ęzłÓM' DOM będących potom kam i elementu o identyfikatorze "menu $$('#menu li');

ww^t^ookgigs.eu w języku JavaScript istnieje pięć podstawowych typów wartości. Wszystko inne jest obiektem. Typami podstawowymi są: • tekst (string), • wartość logiczna (bool ean).

Wartości W ięcej na: www.ebookgigs.eu

|

25

• l i c z b a (number),

• null, • undefined.

Wszystko, co znajdzie się między cudzysłowami lub apostrofami, jest trak­ towane jako tekst. W przeciwieństwie do PHP nie ma żadnych różnic między tekstem ujętym w cudzysłowy i apostrofy. Literały true i fal se umieszczone poza cudzysłowam i to wartości logiczne odpowiednio praw dy i fałszu. W języku JavaScript nie istnieje podział na różne rodzaje liczb, więc nie ma liczb całkowitych i zmiennoprzecinkowych pojedynczej lub podwójnej precyzji — są po prostu liczby. Znak kropki (.) służy do oddzielenia części całkowitej od ułamkowej (na przykład 3.14). Umieszczenie przed liczbą znaku O powoduje potraktowanie wartości, jakby była zapisana w systemie ósemkowym: var a = 012; a === 10; //tru e

W artości w zapisie ósemkowym są obecnie wycofywane i nie można ich stosować w trybie ścisłym w ES5 (więcej informacji na temat trybu ścisłego zawiera rozdział 6 .).

Umieszczenie przed liczbą przedrostka Ox powoduje traktowanie wartości tak, jakby była zapisana szesnastkowo: var a = Oxff; a === 255; //tru e

Dopuszczalne jest również stosowanie notacji naukowej: var a a === var b b ===

= 5e3; 5000; = 5E-3; 0.005;

/ / 5 z trzema zerami. //tru e / / Przesunięcie . o trzy cyfry na lew o o d 5. //tru e

Introspekcja typeof Operator typeof umożliwia poznanie typu wartości, którą się aktualnie prze­ twarza: var a = "test"; typeof a; //"string" typeof Oxff; //"num ber" typeof false; //" b o o lea n "

26

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

Czasem operatora typeof używa się jak funkcji (czyli stosuje zapis typeof (a)), ale nie jest to zalecany format. Pamiętaj, że to operator, a nie funkcja. Po­ wód, dla którego zapis jako funkcja działa, wynika z faktu, iż () to również operator (operator grupowania), który pozwala zmienić domyślną kolejność wykonyw ania operacji: 3 * (2 + 1); / / 9 //k o n tra

3 * 2 + 1;

4

//7 w języku JavaScript zapis typeof (a) jest dla operatora typeof tym samym, co w języku PHP echo($a); dla echo $a; (choć z in­ nych powodów). Taka sama sytuacja występuje w języku PHP dla i nc l u de( $ a) i i nc l ude $a. Choc wszystkie podane przykłady z nawiasami działają, są uważane za złe rozwiązanie, bo udają coś, czym w rzeczywistości nie są.

Wartości nuli i undefined Do tej pory pojawiły się przykłady trzecli z pięciu typów podstawowych: tekst, wartość logiczna i liczba. Istnieją jeszcze dwa typy: nul 1 i undefined. undef ined przyjmuje tylko jedną wartość — wartość undef ined. Deklaraqa zmiennej bez inicjalizaq'i wartością powoduje, że otrzymuje wartość undefined. Jeśli funkcja nie zwraca wartości w sposób jawny, JavaScript zwróci dla niej w artość undefined. Kilka przykładów: var a; typeof a; //"undefined" var b = 1; b = undefined; typeof b; //"undefined"

Zauważ, że operator typeof zawsze zwraca wynik w postaci tekstu. W po­ przednim przykładzie b ma wartość undefined, ale typeof b zwraca wartość "undef ined". Osoby zaczynające swoją przygodę z językiem JavaScript często mylą tekst "undefined" z wartością undefined. Zw róć uwagę na następujące różnice: var b; b === undefined; b === "undefined"; typeof b === "undefined"; typeof b === undefined;

//tru e //fa ls e //tru e //fa ls e

Typ nul 1 przyjmuje tylko jedną wartość — wartość nul 1. Rzadko bywa uży­ teczny, ale można go stosować przy wywoływaniu funkqi, które prz)^’mują

Wartości W ięcej na: www.ebookgigs.eu

|

27

dużą liczbę argumentów^ lub gdy chce się jaw nie wskazać różnicę między zmienną niezadeklarow aną i niezainicjalizowaną, poniew aż undefined oznacza każdy z tych dwóch przypadków. Co ciekawe, typeof zwraca "object" dla w artości nul 1: var a = nul 1; typeof a; // "object"

Należy oczekiwać, że w przyszłych wersjach ECMAScript typeof nul 1 będzie zw racał w artość "nul 1".

m\

___ t

Tablice W języku PHP tablice definiuje się następująco: //P H P $a = array(l, 2, 3);

W języku JavaScript pomija się tekst array i stosuje nawiasy kwadratowe: //Jav aS crip t var a = [1, 2, 3];

4

___ tA

w języku PłHlP w wersji 5.4 dodano obsługę krótkiej składni dla tablic, więc m ożliwy jest zapis identyczny jak w przypadku JavaScript: //P H P $a = [1, 2, 3];

W arto również wspomnieć, że język JavaScript obsługuje zapis podobny do znanego z PHP: //Ja v a S c rip t var arr = Array(1, 2, 3); var arrgh = new Array("a", "b", "c"); / / [ I , 2, 3 ] arr; arrgh; //[" a" , "b", "c"]

Szczegóły tej składni zostaną wyjaśnione w dalszej części książki, ale w arto pamiętać, że zaleca się zapis z nawiasami kwadratowym i (literał tablicy).

Podobnie jak w przypadku PHP w tablicy można mieszać dowolne typy wartości, włączając w to inne tablice: //Jav aS crip t var a = [1, "tak", false, null, undefined, [1, 2, 3]];

28

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

w języku JavaScript tablice są obiektami: typeof a; //" o bject"

Dokładniejsze omówienie właściwości i metod obiektów tabłic znajduje się w dałszej części książki. Poniżej przedstawię tyłko kiłka popułarnychi ele­ mentów: var a = [1, 2, 3]; a .1ength; / / 3, p o d ob n ie j a k countQ m' PH P

Niestety, nie można dodawać do tablicy elementów, używając znanej z PHP składni a [ ] . Trzeba przekazać indeks dodawanego elementu: a[3] = 4;

Najczęściej jako nowego indeksu używa się aktualnej długości tablicy: a[a.length] = "pięć";

Alternatywne rozwiązanie polega na użyciu metody push(), która przypo­ mina funkcję array_push() z P H P : a.push(6);

W ynik końcowy po w ykonaniu w szystkich poleceń: a; //[1 , 2, 3, 4, "piąć", 6]

Jak nietrudno się domyślić, dostęp do elementów odbywa się na podstawie indeksu: var b = a[4]; b ; // "piąć "

Tablice asocjacyjne w języku JavaScript nie ma tablic asocjacyjny cli. W ich miejsce stosuje się obiekty. Zapis, który w PHP miałby postać: //P H P $assoc = array('jeden' => 1, 'dwa' => 2); W

języku JavaScript w ygląda następująco: //Jav aS crip t var assoc = {'jeden': 1, 'dwa': 2};

Różnica polega tak naprawdę na użyciu : zamiast => i otoczeniu wartości nawiasami klamrowymi. Apostrofy wokół kluczy — nazw ijm y je właściwościam i, ponieważ tak naprawdę są właściwościami obiektu — najczęściej się pomija. Jeśli klucz Tablice W ięcej na: www.ebookgigs.eu

|

29

jest poprawnym identyfikatorem (czyli może być użyty jako nazwa zmiennej), apostrofy są opcjonalne: var assoc = {jeden: 1, dwa: 2};

Dodanie nowych elementów do tablicy asocjacyjnej (obiektu, słownika): assoc.trzy = 3;

Aby usunąć właściw ość, użyj operatora delete: delete assoc.dwa;

W ES3 nie istnieje łatwy sposób policzenia w szystkich w łaściwości obiektu inny niż przejście przez w szystkie w łaściwości w pętli fo r -in. Więcej szczegółów na ten temat już wkrótce. Aby pobrać w artość właściwości, użyj notacji kropkowej: assoc.jeden; // 1

Alternatywne rozwiązanie polega na użyciu notacji z nawiasami kwadra­ towymi, co przypomina użycie tablicy w PHP: assoc ["jeden"]; //1

Instrukcje warunkowe Składnia instrukcji i f jest w zasadzie taka sama jak w przypadku PHP: var a. b; if (a ==== "witaj" b = "świecie"; } else if (a === b = "widzenia" } else { b = "co?";

Choć w języku PHP występują alternatywne składnie w postaci if-then-el se, w języku JavaScript w ystępuje tylko jedna wersja wskazana powyżej (jeśli nie liczyć operatora trójargumentowego). Nie można więc użyć elseif za­ miast else if.

Operator trójargumentowy Skrócona składnia instrukqi warunkowej, nazywana operatorem trój argu­ mentowym (ponieważ to operator przyjmujący trzy argumenty), również wygląda podobnie:

30

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

var num = 11; var whatisit = (num % 2) ? "nieparzyste" : "parzyste"; num + " jest " + whatisit; / / "11 je s t nieparzyste"

Zapewne po doświadczeniach w PHP unikasz stosowania więcej niż jed ­ nego operatora trójargumentowego bez dodatkowych nawiasów, bo wtedy jego analiza jest znacznie trudniejsza. Warto podkreślić, że w języku Java­ Script pewne przypadki szczególne działają inaczej. Oto przykład: //P H P echo true ? "a" : true ? "b" : "c";

/ / "h"

/ / R ów now ażne w ersje: echo (true ? "a" : true) ? "b" : "c"; / / "h" echo ("a" ) ? "b" : "c"; / / "b"

W JavaScript składnia jest taka sama, ale wynik będzie inny ze względu na inną kolejność w ykonywania operacji: //Jav aS crip t true ? "a" : true ? "b" : "c";

//" a "

/ / RoM’now azna M’ersja: true ? "a" : "Jestem ignorowany"; //" a "

Ścisła kontrola typu Czy zwróciłeś uwagę na to, że wszystkie prezentowane do tej pory przykła­ dy stosowały potrójny znak równości (===)? Porównywanie działa podob­ nie jak w PHP. • Potrójny znak równości oznacza porównanie ścisłe (porównywana jest zarówno wartość, jak i typ). • Podwójny znak równości oznacza porównanie luźne (porównywane są tylko w artości i jeśli to konieczne, jedna z wartości rzutowana jest na typ drugiej w celu przeprowadzenia porów nania). Aby ułatwić testowanie i szybko w ykrywać błędy, warto wiedzieć, jakie typy się porównuje, więc stosowanie === to dobra praktyka. Dzięki temu błędy można w yłapać znacznie szybciej, najczęściej przed udostępnieniem aplikacji szerokiej grupie odbiorców. Co równie istotne, nie trzeba pamiętać w szystkich reguł rzutowania i porów nyw ania, które nie zawsze są takie same jak w języku PHP. Oto kilka przydatnych wartości, które są konwertowane do fałszu w przy­ padku porównywania luźnego:

Instrukcje w arunkowe W ięcej na: www.ebookgigs.eu

|

31

• pusty tekst — • liczba O, • wartość fal se, • nuli, • undefined, • specjalna wartość liczbowa NaN, która oznacza „to nie liczba", podobnie jak stała NANw PHP. W przypadku porów nań nieścisłych o zaskoczenie nietrudno: nuli == undefined; "" == 0;

//tru e //tru e

ale null === undefined; //fa ls e 0 === //fa ls e

ponieważ typeof null === typeof undefined; //fa lse , "object" != = "undefined” typeof 0 === typeof //fa lse , "number" != = "string"

W szystkie obiekty zawsze konwertują się do w artości prawdy. Ponieważ tablice również są obiektami, także i one są rzutowane na w artość true. To zachowanie inne niż w przypadku PHP, w którym pusta tablica traktowana jest jak fałsz: //P H P if (arrayO ) { echo "witaj"; //N ieosią g a ln e w PHP.

} //Ja v a S c rip t if ([]) { console.log('witaj'); // O siągalne w JavaScript.

}

Co ciekawe, nie wszystkie wartości konwertowane do fałszu są sobie równe w porównaniu luźnym: undefined == null; //tru e undefined == 0; //fa ls e

Podsumowując, ułatwisz życie sobie i innym, stosując w każdej sytuacji po­ równanie ścisłe. W ten sposób nie musisz pamiętać reguł rzutowania ani róż­ nic w zględem PHP.

32

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

Konstrukcja switch Instrukcja sterująca swi tch działa w taki sam sposób jak w PHP, ale z jedną różnicą. Poszczególne przypadki są porównywane w sposób ścisły: //Ja v a S c rip t var a = var result = switch (a) { case false: result = "a jest równe false"; break; case a + "witaj": //moiliM ’e je s t stosow anie wyrażeń result = "co?"; break; case "": / / porów n an ie ścisłe result = "a to pusty tekst"; break; default: result =

Wynikiem działania skryptu (wartością w result) będzie "a to pusty tekst", choć w PHP wynikiem byłoby "a je s t równe false".

Konstrukcja try-catch Konstrukcje try-catch również są bardzo podobne do wersji występujący cli w języku PHP. Oto przykład w PHP: //P H P try { throw new Exception('ouch'); } catch (Exception $e) { $msg = $e->getMessage();

}

Oto wersja w języku JavaScript: //Jav aS crip t var msg = ""; try { throw new Error("ojej"); } catch (e) { msg = e.message;

} msg; / / "ojej"

Instrukcje w arunkowe W ięcej na: www.ebookgigs.eu

|

33

Oto kilka istotnych różnic: • Zgłaszanym obiektem jest Error, a nie Exception. • Nie deklaruje się typu w momencie przechwytywania wyjątku. • Korzysta się z w łaściwości message, zamiast w ywoływać metodę get "^MessageO. W języku JavaScript istnieje również częśc final ly konstrukcji (w PHP jest ona dostępna od wersji 5.5), ale w praktyce stosuje się ją niezwykle rzadko, między innymi z pow odu błędów w jej im plementacji w przeglądarce lE : var msg = try { throw new Error("ojej"); } catch (e) { msg += e.message; } finally { msg += " na zakończenie";

} msg; // "ojej na zakoń czen ie"

Polecenia z bloku final ly wykonają się niezależnie od tego, czy blok try zgłosił wyjątek, czy też nie. Dodatkowe informacje na temat zakresu widoczności zmiennych pojawią się w dalszej części książki, ale w arto podkreślić, że blok catch jest wyjątkiem od ogólnej reguły, która wskazuje, że istnieje tylko zakres na poziomie funkcji, a nie bloku. W pow yż­ szym przykładzie e jest widoczne tylko wewnątrz bloku catch. Wyjątek dotyczy tylko zmiennej e. Zmienna zdefiniowana sa­ modzielnie w bloku catch będzie dostępna również poza nim. try { throw new Error(); } catch (e) { var oops = 1;

} typeof e; //"undefined" typeof oops; //"num ber"

Konstrukcja try-catch zmniejsza wydajność wykonywania kodu, jeśli wykon}rwany jest blok catch. Z tego powodu należy jej unikać w ścieżkach wyko­ nania najbardziej czułych na w ydajność (na przykład pętlach).

34

I

Rozdział 2. Składnia języka JavaScript W ięcej na: www.ebookgigs.eu

Pętle while i for Pętle while, do-while i for działają dokładnie w ten sam sposób w języl t iw n c tim i •.er l,B .s d ,L M :» L * l i FT> return m * b * slab 4 l,C « n

rtWTr, ir^iri^binOj ::

» : ^uivctitn §ooi«_ »«ctl«i>s ^unctl«i>. »^*0r>i>4aŁtiiui«i •ytvciicn ^

r il u r .i 1W {S , U ] ; pBpńJOj '.lir ip t . >:=

0.

o

t)

linr 4.

I

Rysunek 3.2. Osiągnięcie punktu lostrzymania w przeglądarce Chrome Zauważ również, że w części Closure z rysunku 3.2 brakuje zmiennej sum. Pow inna się tam znaleźć, ale to błąd zw iązany z oknem inspektora W eb w przeglądarce Chrome. Jeśli to samo zadanie wykona się w przeglądarce Sa­ fari, pojawi się zarówno funkqa sum, jak i obiekt arguments (patrz rysunek 3.3).

56

I

Rozdział 3. Funkcje W ięcej na: www.ebookgigs.eu

Kliknięcie przycisku odtwarzania w debuggerze spowoduje przejście do dru­ giego punktu wstrzymania w wierszu 21. Na liście zakresów pojawiły się dwie części Closure, ponieważ funkqa innerSum() znajduje się wewnątrz 5um(), więc tworzy jeszcze jeden element łańcucha zakresów (patrz rysunek 3.4).

jan p«riAC>

1

(b,

hiH^[i'‘ęD,*prę ■ it e j SM lu nctlen b-) ( pfeu-ii n ti ■ > 4. hgndr^tl h: l]

Ttl*5U-d

^ DOM Crr.-'7-'-i=

rrłlnnc--5iw: ■ njj

>XIIRBi«lcpc^M F

i U^Lśkpt-in:!.

ig C SI, (D Rysunek 3.4. Osiągnięcie drugiego punktu wstrzymania

Zachowanie zakresu Wiemy już, że funkqe lokalne mają dostęp do zmiennych środowiska, w któ­ rym były definiowane. Gdy wykonywanie kodu przenosi się poza funkcję lokalną, na przykład innerSum() lub sum(), jej zakres lokalny (czyli jej obiekt __variables) nie jest dostępny i może zostać zniszczony, o ile nikt go już nie potrzebuje. To bardzo interesująca informacja — jest bardzo użyteczna, ale i ułatwia popełnianie błędów. Załóżmy, że sum() nie jest funkcją lokalną, ale globalną. Jej definicja nadal występuje w środowisku lokalnym, ale sama zmienna sum została zadekla­ rowana w zakresie globalnym: var global_ten = 10; var sum; function papa() { var hundred_more = 100; sum = function (a, b) {

Łańcuch zakresu widoczności zmiennych W ięcej na: www.ebookgigs.eu

57

return a + b + global_ten + hundred_more;

}; return sum(9, 11);

papa O ; //1 3 0

Po wykonaniu funkcji papa() z poziom u zakresu globalnego można w y­ w ołać całkowicie now ą funkcję sum(). W momencie jej wykonywania ma dostęp do w szystkich zmiennych globalnych, takich jak gl obal_ten (i to bez użycia specjalnej składni, na przykład $GLOBAL w PHP). Niektórych może jednak zaskoczyć fakt, iż ma dostęp do zmiennej hundred_more i może od­ czytać jej wartość: sum(9, 11); / / J 3 0

Dlaczego tak się dzieje? Co stało się z lokalnym zakresem po wyjściu z kodu funkcji papa O ? W poprzednim przykładzie, w którym to sum() było zmienną lokalną, zakres lokalny został po prostu usunięty, ponieważ nikt go nie potrzebował. Gdy jednak sumO jest dostępne globalnie i zostało zdefinio­ wane w papa O, wtedy właściw ość sum.__scope odnosi się do lokalnego za­ kresu papa O . Z tego powodu zakresu nie można usunąć z pamięci — musi być nadal dostępny, ponieważ referencję do niego przechowuje właściwość sum.__scope.

W przypadku domknięć (funkqi) w języku JavaScript warto pamiętać o jednej ważnej kwestii — funkqe przechowują referenqę do środowiska, w którym były definiowane, czyli tak zwanego środowiska leksykalnego. Funkcja papa() rów nież przechow uje referencję do środow iska definiują­ cego, ale w tym przypadku jest to zakres globalny, do którego zawsze ma się dostęp.

Przechowywane są referencje, a nie wartości W arto zdaw ać sobie spraw ę z tego, że tak napraw dę przechow yw ane są tylko referencje do środowiska, a nie konkretne wartości. Sprawdźmy to na przykładzie: var global_ten = 10; var sum; function papa() { var hundred_more = 100; sum = function (a, b) { return a + b + global_ten + hundred_more;

};

58

I

Rozdział 3. Funkcje W ięcej na: www.ebookgigs.eu

hundred_more = 100000; / / To różnica względem poprzedniego przykładu.

} papa O ; // Tym razem nie zw racam y w artości M’ sposób jaMmy. sum(9, 11); //1 0 0 0 3 0

W momencie definiowania sum() zmienna hundred_more zawierała wartość 100, ale nieco później zm ieniono jej w artość na 100000. W m om encie pow rotu z funkqi papa() jej zakres lokalny został uaktualniony nową wartością 100000. To zakres, do którego dostęp ma sum(). Funkcja 5um() ma zawsze dostęp do najbardziej aktualnego stanu środowiska definiującego. Zwróć uwagę na różnicę względem języka PHP, w którym to „lokalizuje się" zmienną przy użyciu use: $hundred_more = 100; $sum = function ($a, $b) use ($hundred_more) { return $a + $b + $hundred_more;

}; $hundred_more = 100000; echo $sum(0, 0); //1 0 0

Ten sam kod w języku JavaScript zwróci w artość 100000, ponieważ funkcja przechowuje jedynie referencję do zmiennej hundred_more, a nie jej wartość: var hundred_more = 100; var sum = function (a, b) { return a + b + hundred_more;

}; hundred_more = 100000; sum (O, 0); //1 0 0 0 0 0

Domknięcia W pętli Przyjrzyjmy się jeszcze jednem u przykładowi, w którym doskonale widać różnicę w działaniu domknięć. Zdefiniujm y tablicę funkcji: var fns = []; function definer() { for (var i = 0; i < 5; i++) { fns.push(function () { return i;

}); } } definerO ;

Łańcuch zakresu widoczności zmiennych W ięcej na: www.ebookgigs.eu

|

59

Przykład definiuje w pętli pięc funkcji. Można je w yw ołać w następujący sposób: fns[0](); fns[l](); fns[2](); fns[3](); fns[4]();

Jeśłi nie czytałeś zbyt uważnie poprzedniego fragmentu książki, możesz sądzić, że każde z wywołań funkqi zwróci wartość i z momentu definiqi funkcji. W rzeczyw istości w szystkie pięć funkcji zostało zdefiniow anycłi w tym samym środowisku, więc ma dostęp do tego samego środowiska łokałnego. W momencie zakończenia działania funkqi definer(), i ma wartość 5, a nie 4, ponieważ po ostatniej iteraqi została jeszcze wykonana operaqa i++. To wła­ śnie ta wartość pozostaje w udostępnionym środowisku. Widzi ją wszystkie pięć funkcji, więc w szystkie zw rócą w artość 5: fns[0](); fns[l](); fns[2](); fns[3] O ;

//5 //5 //5 //5

fns[4] O ; //5

Co zrobić, aby kołejne funkcje zwracały coraz to w iększą wartość? Trzeba użyć jeszcze jednego domknięcia, by zapewnić „łokałną" wersję i , czyłi w tym przypadku zmienną local_i: var fns = []; function definer() { for (var i = 0; i < 5; i++) { fns.push(function (local_i) { return function () { return local_i;

}; }(i));

} definerO ;

Przykład wprowadził dodatkowy obiekt zakresu. W nowym zakresie wystę­ puje zmienna łokałna 1ocal_1. Jest iniqałizowana aktuałną wartością zmiennej 1 i zapamiętywana. W ten sposób każda z pięciu funkqi posiada własne, nieza­ leżne środowisko.

60

I

Rozdział 3. Funkcje W ięcej na: www.ebookgigs.eu

Teraz wyniki działania funkcji są zgodne z oryginalnymi oczekiwaniami: fns[0](): fns[l]() fns[2]() fns[3]() fns[4]()

//O //I //4

W prowadźmy pewne usprawnienie i uprośćmy pętlę wewnątrz definer(). Wprowadźmy funkqę pomocniczą binder(), która zwraca nową funkcję z lo­ kalną wartością i : var fns = []; function binder(i) { return function () { return i;

}; function definer() { for (var i = 0; i < 5; i++) { fns.push(binder(i));

} definerO ; // Test. fns[0]() fns[l]() fns [2] O fns [3] O fns [4] O

//O //I //2

Ćwiczenie — pętla z onclick Przyjrzyjmy się bardziej praktycznem u przykładowi ilustrującem u odno­ szenie się do zmiennych środowiska przez referencję, a nie przez wartość. Przypuśćmy, że na stronie HTML umieściliśm y trzy przyciski ze zw ięk­ szającymi się w artościami atrybutu i d: jeden dwa trzy

Następnie przypisujem y w pętli

for

procedury obsługi zdarzeń

click:

for (var i = 1; i = 0) { index--;

} return data [index];

}; rewind = function () { index = -1;

}; }()); W przedstawionym przykładzie index, data i count są zmiennymi prywat­ nymi, ponieważ użytkownicy iteratora potrzebują jedynie metod dotyczą­ cych poruszania się w przód i w tył, więc najczęściej nie powinni miec do­ stępu do danycłi i wskaźnika, bo tyłko byłoby to my łące. Przetestujmy poruszanie się w przód i w tył: next O ; //" jed en " next O ; //"dw a" previousO; //"trzy"

Oto dodatkowy test — przewijamy do początku i zaczynamy wszystko raz jeszcze: var a; rewind O ; while (a = next()) { // Wykonaj o p era cje na 'a'.

Przekazywanie i zwracanie funl '); / / " 5 > 4 > 3 " [5, 4, 3].join(); //"5,4,3" // taki sam M’y n ikj a k M'przypadku użycia toStringQ [5, 4, 3] .joinC '); //"543"

W PHP przeciwieństwem funkcji implode() jest explode(), a w JavaScript prze­ ciwieństwem metody Array.prototype.join() jest Array.prototype.spl i t ( ) , o której w krótce dowiesz się nieco więcej.

Konstruktor RegExp Konstruktor RegExp tworzy obiekty wyrażeń regularnych. Może byc stosowa­ ny zarówno ze słowem kluczowym new^ jak i bez niego. Co więcej, istnieje również alternatywna składnia wykorzystująca literał wyrażenia regularnego: var re = new RegExp('[a-z]'); //kon struktor var re = RegExp ('[a-z]'); //kon struktor bez'ne-w' var re = /[a-z]/; //lite r a ł

Jak nietrudno zauw ażyć, literał jest krótszy i prostszy. Konstruktor jest niezbędny tylko w sytuacji, gdy w zorzec wyrażenia regularnego powstaje w trakcie działania programu z łączenia kilku tekstów. W ES3 obiekty wyrażeń regularnych zapisane w notacji literałowej są tworzone tylko raz w fazie przetwarzania kodu źródłowego: function gimme() { return /a-z/;

} var a = gimme(); var b = gimmeO ; a === b; //fa lse , a le w ES3 true

Przy tworzeniu obiektu można wskazać jeden z trzech modyfikatorów w y­ rażenia regularnego: • gl obal (litera g) — poszukuje wszystkich wystąpień, a nie tylko pierw­ szego; ^y^^^ii^ti 1 i ne (litera

^ ^ stąp ień mogących obejmować kilka

• ignoreCase (litera i) — poszukuje wystąpienia bez względu na użytą

wielkość liter. M odyfikatory można podać w dowolnej kolejności. Domyślnie wszystkie przyjm ują wartość fal se:

Konstruktory wbudowane W ięcej na: www.ebookgigs.eu

|

105

var re = new RegExp('[a-z]', 'gmi'); var re = /[a-z]/ig; // test re.ignoreCase; //tru e re.multil ine; //fa ls e

Metoda test() i właściwości obiektu Po utworzeniu obiektu wyrażenia regularnego do przeprowadzenia w y­ szukania używa się metod exec() i te s t( ) . Metoda exec() zwraca dopaso­ wania, a metoda te s t() zwraca jedynie wartość logiczną informującą, czy znaleziono przynajmniej jedno dopasowanie: re.exec("somestring"); / / zw raca dopasow an ia re.test("somestring"); / / ZM'raca true\false

Dodatkowe w łaściwości obiektu w yrażenia regularnego są następujące: re. 1astlndex; //In d ek s ostatniego dopasow ania. re.source; //W yrażen ie regularne ja k o tekst (n ap rzy klad "[a-z]"). re.gl obal ; //C z y ustawiono m odyfikator g? re.mul ti 1 ine; //Czy ustawiono m odyfikator m? re.ignoreCase; //Czy ustawiono m odyfikator i?

Metoda exec() M etoda exec() po wykonaniu swych zadań zwraca w artość będącą tablicą znalezionych dopasowań, ale zawiera dodatkowo dwie właściwości: input i index, które zawierają odpowiednio analizowany tekst i indeks dopasowania. Przeanalizujm y metodę, używając przykładu: var re = /([oa]) (n+)/; var result = re.exec("wonna manny"); result.joinC , '); / / "onn, o, nn"

Jak łatwo zauważyć, result to tablica. Zawiera zarówno pełne dopasowanie (onn), jak i dopasowania z poszczególnych fragmentów grupujących w yra­ żenia regularnego. Tablica result zaw iera dodatkow o w łaściw ości input i index: resul t .input; // "wonna manny" result.index; / / I

Tablice są obiektami, więc nie ma żadnych przeszkód, by za­ wierały dodatkowe właściwości poza indeksami num erycznymi O, 1 , 2 . . .

106

I

Rozdział 5. W budowane interfejsy programistyczne W ięcej na: www.ebookgigs.eu

Można ponownie w yw ołać re.exec(), ale otrzyma się identyczny wynik. Wyrażenie regularne powinno również dopasować się do fragmentu ann. Aby otrzymać w szystkie dopasowania, niezbędny jest modyfikator g: var re = /([oa]) (n+)/g;

Teraz metodę exec() można wywołać w pętli i otrzymać następne dopaso­ wanie. W każdym dopasowaniu w łaściw ość r e .l astlndex otrzymuje nową w artość w skazującą indeks znaku kończącego dopasowanie. Gdy metoda re.execO zwróci nuli (co dodatkowo powoduje ponowne w stawienie do re .l astlndex wartości 0), to sygnał, że nie udało się odnaleźć następnego dopasowania: var re = /([oa]) (n+)/g; var str = "wanna manny"; re.l astlndex; //O re.exec(str); / / ["onn", "o", "nn"] re. 1astlndex; / / 4 re.exec(str); //["ann", "a", "nn"] re. 1astlndex; //10 re.exec (str); //n u li re. 1astlndex; //O

Ten sam przykład w postaci pętli: var re = /([oa]) (n+)/g; var str = "wanna manny"; var match; while (match = re.exec(str)) { / / Skorzystaj z 'match ’ lub 're.lastlndex'. console.log(match); console.log(re.lastlndex);

Konstruktor Function Konstruktor Function służy do tworzenia obiektów funkcji: var f = new Function('a, b', 'return a + b'); f(4, 5); / / 9

Nietrudno się domyślić, że ten sposób tworzenia funkqi nie jest zbyt wygod­ ny i mocno przypomina eval (), więc należy go unikać, jeśli to tylko możliwe. Choć Function O można użyć w sensowny sposób w niektórycli zadaniach dotyczących metaprogramowania, dla w iększości typowych problemów

Konstruktory wbudowane W ięcej na: www.ebookgigs.eu

|

107

JavaScript jest wystarczająco dynamiczny^ by trzeba się było uciekać do pisania kodu jako tekstu do dodatkowego przetworzenia. Jednym z możliwych zastosowań Function() jest zastąpienie eval (), ponieważ Function O tworzy nowy zakres lokalny, więc zmienne po wykonaniu kodu nie zmieszają się ze zmiennymi globalnymi. Oto przykład: var code = "var tmp = 1; console.1o g ( t m p ) ; Function(code) (); //W y.^vietla 1. typeof tmp; //u n d efin ed eval (code); // Wyświetla 1. typeof tmp; //"num ber"

T m róć uwagę, że w przypadku konstruktora Function() również można po­ minąć słowo kluczowe new. Konstruktor Function() nie tylko nie umieszcza zmiennych w zakresie glo­ balnym , ale również nie posiada w swoim łańcuchu zakresów niczego po­ za zakresem globalnym: // G lobaln a przestrzeń nazw. var globe = "okrągTy"; (function () { var globe = "płaski"; (new FunctionC'return globe;")) (); //"okrągły" eval ("return globe;"); //" p łaski"

}());

Właściwości obiektu Function w zasadzie omówiliśmy już wcześniej w iększość w łaściwości i metod obiektu funkcji: • 1ength — oczekiwana liczba argumentów;

• name — nazwa funkcji (nie stanowi części standardu ECM AScript); • cali O — w ykonuje funkcje przez przekazanie poszczególnych argu­ mentów; • apply O — w ykonuje funkcję, prz)^’mując wszystkie argum enty do przekazania jako tablicę. Oto krótki przykład ilustrujący użycie wymienionych właściwości i metod: //D efinicja funkcji. function sum() { var res = 0;

108

I

Rozdział 5. W budowane interfejsy programistyczne W ięcej na: www.ebookgigs.eu

for (var i = 0; i < arguments.length; i++) { res += arguments[i];

} return res;

// Test M’lasciM'osci. sum.length; //O sum. name; //"sum " / / Test metod. sum.call (null, 1, 2, 3); //6 sum.apply(nul 1, [2, 2, 2, 2]); / / 8

Pierwszym argumentem przekazyw anym do c a ll() i apply() jest obiekt udostępniany później w kodzie funkcji pod nazw ą th is. Jeśli this nie jest używane, warto przekazać wartość nul 1.

Konstruktor String Konstruktor StringO użyty w połączeniu ze słowem kłuczowym new tworzy obiekt tekstu. Konstruktor StringO użyty bez tego słowa kluczowego rzutuje argum ent na tekst jako typ podstawowy. Obiekt tekstu nie jest równo­ znaczny typowi prostemu tel
Stefanow S. - Javascript dla programistów PHP

Related documents

154 Pages • 37,451 Words • PDF • 8.5 MB

238 Pages • 55,625 Words • PDF • 32 MB

92 Pages • 15,315 Words • PDF • 2.6 MB

205 Pages • 18,248 Words • PDF • 648.8 KB

44 Pages • 7,119 Words • PDF • 2.5 MB

379 Pages • 92,205 Words • PDF • 1.3 MB

240 Pages • PDF • 43.9 MB

14 Pages • 2,115 Words • PDF • 634.8 KB

108 Pages • PDF • 18.8 MB

6 Pages • 279 Words • PDF • 98.5 KB

146 Pages • PDF • 62.9 MB