Datei wird geladen, bitte warten...
Zitiervorschau
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
Tytuł oryginału: JavaScript for PHP Devełopers Tłumaczenie: Rafał Jońca ISBN: 978-83-246-8220-1 © 2013 ITełion S.A. Authorized Połisłi translation of the Engłish edition of JavaScript for PITP Developers, ISBN 9781449320195 © 2013 Stoyan Stefanov. This translation is published and sold by permission of O'Reilly Media, Inc., which owns or controls all rights to sell the same. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmow}^!, magnetyczn}^! lub inn3mi 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 Ic, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: [email protected] WWW: http://heIion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenic tę książkę, zajrzyj pod adres http: //helion.pl/user/opinie/]avphp_ęhook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Poleć książkę na Facebook.com
• Księgarnia internetowa
• Kup w wersji papierowej
• Lubię to!» Nasza społeczność
• Oceń książkę
W ięcej na: www.ebookgigs.eu
Dla Nathalie, Zlatiny i Evy
W ięcej na: www.ebookgigs.eu
Więcej na: www.ebookgigs.eu
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:
M ogłoby się wydawać^ ż e przedstawiony kod zadziała prawidłowo, ałe ]