Wprowadzenie
Podstawę biblioteki jQuery stanowi JavaScript, który sam w sobie jest niezwykle bogatym i ekspresyjnym językiem. W poniższej sekcji poznamy podstawowe pojęcia języka JavaScript, a także przyjrzymy się czekającym na nowicjuszy pułapkom. Omówione zagadnienia skierowane są przede wszystkim do początkujących, jednak zapoznanie się z charakterystycznymi cechami JavaScriptu może przynieść korzyści również programistom mającym doświadczenie w innych językach programowania.
Jeśli chcesz dowiedzieć się więcej na temat języka JavaScript, gorąco polecam książkę Douglasa Crockforda JavaScript: The Good Parts oraz nasz kurs programowania w języku JavaScript autorstwa Marijna Haverbeke
Podstawy składni JavaScriptu
Omówienie instrukcji, nazewnictwa zmiennych, białych znaków oraz innych podstawowych elementów składni języka JavaScript.
Przykład 2.1. Prosta deklaracja zmiennej
var foo = 'witaj, świecie';
Przykład 2.2. Poza cudzysłowem białe znaki nie pełnią żadnej funkcji
var foo = 'witaj, świecie';
Przykład 2.3. Nawiasy oznaczają pierwszeństwo wykonywania działania
2 * 3 + 5; // zwraca 11; pierwsze wykonywane jest mnożenie
2 * (3 + 5); // zwraca 16; pierwsze wykonywane jest dodawanie
Przykład 2.4. Tabulatory zwiększają czytelność kodu, lecz nie mają żadnego specjalnego znaczenia
var foo = function() {
console.log('witaj');
};
Operatory JavaScript
Podstawowe operatory
Używając podstawowych operatorów możemy manipulować wartościami.
Przykład 2.5. Łączenie (konkatenacja)
var foo = 'witaj,';
var bar = 'świecie';
console.log(foo + ' ' + bar); // witaj, świecie
Przykład 2.6. Mnożenie i dzielenie
2 * 3;
2 / 3;
Przykład 2.7. Inkrementacja i dekrementacja
var i = 1;
var j = ++i; // preinkrementacja: j równa się 2; i równa się 2
var k = i++; // postinkrementacja: k równa się 2; i równa się 3
Operacje na liczbach i ciągach znaków
Czasem liczby i ciągi w języku JavaScript mogą zachowywać się nie do końca zgodnie z naszymi przewidywaniami.
Przykład 2.8. Dodawanie vs. łączenie
var foo = 1;
var bar = '2';
console.log(foo + bar); // 12, ups
Przykład 2.9. Jak sprawić, by ciąg stał się liczbą
var foo = 1;
var bar = '2';
// wymuszamy, aby ciąg stał się liczbą
console.log(foo + Number(bar));
Wywołanie konstruktora Number
jako funkcji (tak jak w przykładzie powyżej) sprawi, że jego argument będzie traktowany jako liczba. Ten sam rezultat osiągniemy używając jednoargumentowego operatora plus:
Przykład 2.10. Jak sprawić, by ciąg stał się liczbą (przy użyciu jednoargumentowego operatora plus)
console.log(foo + +bar);
Operatory logiczne
Operatory logiczne umożliwiają oszacowanie argumentów poprzez operacje I oraz LUB.
Przykład 2.11. Operatory logiczne I oraz LUB
var foo = 1;
var bar = 0;
var baz = 2;
foo || bar; // zwraca 1, czyli prawdę
bar || foo; // zwraca 1, czyli prawdę
foo && bar; // zwraca 0, czyli fałsz
foo && baz; // zwraca 2, czyli prawdę
baz && foo; // zwraca 1, czyli prawdę
Być może przykład nie ilustruje działania operatorów zbyt jasno, zatem krótkie wyjaśnienie: operator ||
zwraca wartość pierwszego argumentu, który jest prawdziwy, zaś w przypadku, gdy obydwa argumenty są fałszywe, zwracany jest ostatni argument. Operator &&
zwraca wartość pierwszego fałszywego argumentu, lub wartość ostatniego, jeśli obydwa argumenty są prawdziwe.
Nie zapomnij zajrzeć do sekcji „Rzeczy prawdziwe, rzeczy fałszywe”, aby dowiedzieć się więcej na temat tego, które wartości są prawdziwe (true
), a które fałszywe (false
).
Operatory porównywania
Dzięki operatorom porównywania możemy sprawdzić, czy dane wartości są sobie równe bądź ze sobą identyczne.
Przykład 2.12. Operatory porównywania
var foo = 1;
var bar = 0;
var baz = '1';
var bim = 2;
foo == bar; // zwraca fałsz
foo != bar; // zwraca prawdę
foo == baz; // zwraca prawdę; uwaga!
foo === baz; // zwraca fałsz
foo !== baz; // zwraca prawdę
foo === parseInt(baz); // zwraca prawdę
foo > bim; // zwraca fałsz
bim > baz; // zwraca prawdę
foo <= baz; // zwraca prawdę
Kod warunkowy
Czasami chcemy, aby blok kodu został wykonany tylko w określonej sytuacji. Umożliwia nam to kontrola przepływu sterowania za pomocą instrukcji if-else
.
Przykład 2.13. Sterowanie wykonaniem programu
var foo = true;
var bar = false;
if (bar) {
// ten kod nigdy nie zostanie wykonany
console.log('witaj!');
}
if (bar) {
// kod nie zostanie wykonany
} else {
if (foo) {
// kod zostanie wykonany
} else {
// kod zostanie wykonany jeśli zarówno foo i bar będą fałszywe
}
}
Rzeczy prawdziwe, rzeczy fałszywe
Aby umiejętnie sterować działaniem programu, należy wiedzieć, które wartości są prawdziwe, a które fałszywe. Zdarza się, że wartości, które pozornie wydają się fałszywe bądź prawdziwe zwracają przeciwną wartość.
Przykład 2.14. Wartości, które zwracają prawdę
'0';
'dowolny ciąg';
[]; // pusta tablica
{}; // pusty obiekt
1; // dowolna liczba różna od zera
Przykład 2.15. Wartości, które zwracają fałsz
0;
''; // pusty ciąg
NaN; // zmienna, która nie jest typu liczbowego
null;
undefined; // uwaga – undefined może mieć zmienioną definicję!
Przypisanie warunkowe zmiennych za pomocą operatora trójargumentowego
Czasami chcemy przypisać zmiennej daną wartość w zależności od tego, czy został spełniony określony warunek. Możemy wówczas użyć instrukcji warunkowej if-else
, lecz w wielu przypadkach znacznie wygodniejszym rozwiązaniem jest operator trójargumentowy. [Definicja: operator trójargumentowy sprawdza, czy został spełniony określony warunek; jeśli tak, zwracana jest odpowiednia wartość, jeśli nie — inna.]
Przykład 2.16. Operator trójargumentowy
// jeśli bar jest prawdziwe, niech foo równa się 1
// w przeciwnym wypadku niech foo będzie równe 0
var foo = bar ? 1 : 0;
Operatora trójargumentowego można używać bez przypisania zmiennej wartość zwrotnej, jednak lepiej tego nie robić.
Instrukcja switch
Czasami zamiast kilku bloków if/else-if/else
wygodniej jest użyć instrukcji switch
. [Definicja: instrukcja switch
sprawdza wartość danego wyrażenia bądź zmiennej i wykonuje poszczególne bloki kodu zależnie od tej wartości.]
Przykład 2.17. Instrukcja switch
switch (foo) {
case 'bar':
alert('wartość bar – ojej!');
break;
case 'baz':
alert('buu baz :(');
break;
default:
alert('wszystko pozostałe może być');
break;
}
Instrukcja switch
nie jest już zbyt chętnie wykorzystywana przez programistów JavaScript, ponieważ często do osiągnięcia tych samych rezultatów można wykorzystać obiekty. Są one wygodniejsze do ponownego użycia czy testów. Na przykład:
var coMamZrobic = {
'bar' : function() {
alert('wartość bar – ojej!');
},
'baz' : function() {
alert('buu baz :(');
},
'default' : function() {
alert('wszystko pozostałe może być');
}
};
if (coMamZrobic[foo]) {
coMamZrobic[foo]();
} else {
coMamZrobic['default']();
}
Obiektom przyjrzymy się bliżej w dalszej części rozdziału.
Pętle języka JavaScript
Dzięki pętlom możemy wykonać blok kodu określoną liczbę razy.
Przykład 2.18. Pętle
// rejestruje 'próba 0', 'próba 1', ..., 'próba 4'
for (var i=0; i<5; i++) {
console.log('próba ' + i);
}
var
przed nazwą zmiennej i, to jednak „zakres” zmiennej nie ogranicza się tylko do bloku pętli. Pojęcie zakresu zmiennych zostanie dokładniej omówione w dalszej części rozdziału.Pętla for
Pętla for
składa się z czterech instrukcji. Jej struktura wygląda następująco:
for ([inicjacja]; [warunek]; [iteracja])
[trescPetli]
Instrukcja inicjująca wykonywana jest tylko raz, przed rozpoczęciem pętli. Wówczas możemy przygotować lub zadeklarować jakieś zmienne.
Instrukcja warunkowa wykonywana jest przed każdą iteracją, a od jej wartości zwrotnej zależy dalsze wykonywanie pętli. W przypadku wartości fałszywej pętla się zatrzymuje.
Instrukcja iteracja wykonywana jest na końcu każdej iteracji — wtedy też można zmienić stan istotnych zmiennych. Zazwyczaj zwiększa bądź zmniejsza się wówczas licznik, co przybliża pętlę do końca.
TrescFunkcji wykonywana jest w czasie każdej iteracji i może zawierać dowolny kod. Zazwyczaj treść funkcji składa się z wielu instrukcji, które muszą zostać wykonane i są otoczone klamrami ({...}
).
Oto typowa pętla for
:
Przykład 2.19. Typowa pętla for
for (var i = 0, limit = 100; i < limit; i++) {
// Ten blok kodu zostanie wykonany 100 razy
console.log(Iteracja nr ' + i);
// Uwaga: jako ostatni wyświetli się komunikat "Iteracja nr 99"
}
Pętla while
Pętla while
swoją budową przypomina instrukcję warunkową if
, przy czym treść tej pętli jest wykonywana aż do momentu, w którym dany warunek nie jest już dłużej spełniany.
while ([warunek]) [trescPetli]
Oto typowa pętla while
:
Przykład 2.20. Typowa pętla while
var i = 0;
while (i < 100) {
// Ten blok kodu zostanie wykonany 100 razy
console.log('Iteracja nr ' + i);
i++; // inkrementacja i
}
Z pewnością zauważysz, że w treści pętli musimy dokonać inkrementacji licznika. Licznik można połączyć z warunkiem, tak jak zaprezentowano poniżej:
Przykład 2.21. Pętla while
, w której licznik jest inkrementowany po sprawdzeniu na nim warunku
var i = -1;
while (++i < 100) {
// Ten blok kodu zostanie wykonany 100 razy
console.log('Iteracja nr ' + i);
}
Zwróć uwagę, że zaczynamy z poziomu -1
i korzystamy z przedrostkowego operatora inkrementacji (++i
), czyli preinkrementacji.
Pętla do-while
Jest to praktycznie taka sama pętla co while
— różnica polega na tym, że treść pętli do-while
jest wykonywana co najmniej jeden raz, przed sprawdzeniem warunku.
do [trescPetli] while ([warunek])
Oto pętla do-while
:
Przykład 2.22. Pętla do-while
do {
// Nawet w przypadku niespełnienia warunku
// treść pętli zostanie wykonana raz.
alert('Hej!');
} while (false);
Pętle tego typu wykorzystywane są dość rzadko, ponieważ tylko w nielicznych wypadkach chcemy, aby pętla została wykonana bez sprawdzenia jakiegokolwiek warunku. Mimo wszystko o istnieniu pętli do-while
warto wiedzieć.
Instrukcje break i continue
Zazwyczaj zatrzymanie pętli następuje po niespełnieniu określonego warunku, lecz można do tego również doprowadzić umieszczając w treści funkcji instrukcję break
.
Przykład 2.23. Zatrzymywanie pętli
for (var i = 0; i < 10; i++) {
if (coś) {
break;
}
}
Możemy również chcieć kontynuować pętlę bez wykonywania jej dalszej treści — służy do tego instrukcja continue
.
Przykład 2.24. Przeskakujemy do następnej iteracji pętli
for (var i = 0; i < 10; i++) {
if (coś) {
continue;
}
// Poniższa instrukcja zostanie wykonana tylko wówczas,
// jeśli warunek coś nie zostanie spełniony
console.log('Koniec');
}
Słowa zarezerwowane
W języku JavaScript istnieje pewna liczba „słów zarezerwowanych” — są to słowa o specjalnym znaczeniu. Słów tych należy używać w kodzie tylko zgodnie z ich określonym znaczeniem.
abstract
boolean
break
byte
case
catch
char
class
const
continue
debugger
default
delete
do
double
else
enum
export
extends
final
finally
float
for
function
goto
if
implements
import
in
instanceof
int
interface
long
native
new
package
private
protected
public
return
short
static
super
switch
synchronized
this
throw
throws
transient
try
typeof
var
void
volatile
while
with
Tablice JavaScript
Tablice to indeksowane od zera listy wartości. Dzięki tablicom można w wygodny sposób przechowywać zbiór związanych ze sobą elementów tego samego typu (na przykład ciągów), choć w rzeczywistości w tablicy mogą znajdować się różne typy elementów, w tym inne tablice.
Przykład 2.25. Prosta tablica
var mojaTablica = [ 'witaj', 'świecie' ];
Przykład 2.26. Dostęp do tablicy poprzez indeks
var mojaTablica = [ 'witaj', 'świecie', 'foo', 'bar' ];
console.log(mojaTablica[3]); // rejestruje 'bar'
Przykład 2.27. Sprawdzamy rozmiar tablicy
var mojaTablica = [ 'witaj', 'świecie' ];
console.log(mojaTablica.length); // rejestruje 2
Przykład 2.28. Zmiana wartości elementu
var mojaTablica = [ 'witaj', 'świecie' ];
mojaTablica[1] = 'zmieniony';
Przykład 2.29. Dodawanie elementów do tablicy
var mojaTablica = [ 'witaj', 'świecie' ];
mojaTablica.push('nowy element');
Przykład 2.30: Korzystanie z tablic
var mojaTablica = [ 'w', 'i', 't', 'a', 'j' ];
var mojCiag = mojaTablica.join(''); // 'witaj'
var mojPodzial = mojCiag.split(''); // [ 'w', 'i', 't', 'a', 'j' ]
Obiekty JavaScript
Obiekty zawierają jedną lub więcej par klucz-wartość. Kluczem może być dowolny ciąg, zaś wartość może stanowić dowolny typ wartości: liczba, ciąg, tablica, funkcja, a nawet inny obiekt.
Definicja: Kiedy jedna z wartości jest funkcją, nazywa się ją metodą danego obiektu. W pozostałych przypadkach wartości nazywane są własnościami lub atrybutami.
Jak się okazuje, praktycznie wszystko w języku JavaScript jest obiektem — tablice, liczby, funkcje, a nawet ciągi. Każde z nich ma swoje własności i metody.
Przykład 2.31. Tworzymy „literał obiektowy”
var mojObiekt = {
przywitajSie : function() {
console.log('witaj');
},
mojeImie : 'Joanna'
};
mojObiekt.przywitajSie(); // rejestruje 'witaj'
console.log(mojObiekt.mojeImie); // rejestruje 'Joanna'
Funkcje JavaScript
Funkcje służą do przechowywania bloków kodu, który ma być wykorzystany wielokrotnie. Mogą one przyjmować od zera do kilku argumentów, a także zwracać wartość.
Funkcje można tworzyć na różne sposoby:
Przykład 2.32. Definicja funkcji
function foo() { /* zrób coś */ }
Przykład 2.33. Nazwane wyrażenie funkcyjne
var foo = function() { /* zrób coś */ }
Korzystanie z funkcji
Przykład 2.34. Prosta funkcja
var przywitaj = function(osoba, powitanie) {
var tekst = powitanie + ', ' + osoba;
console.log(tekst);
};
przywitaj('Rebecca', 'Witaj');
Przykład 2.35: Funkcja zwracająca wartość
var przywitaj = function(osoba, powitanie) {
var tekst = powitanie + ', ' + osoba;
return tekst;
};
console.log(przywitaj('Rebecca','witaj'));
Przykład 2.36. Funkcja zwracająca inną funkcję
var przywitaj = function(osoba, powitanie) {
var tekst = powitanie + ', ' + osoba;
return function() { console.log(tekst); };
};
var pozdrowienie = przywitaj('Rebecca', 'Witaj');
pozdrowienie();
Samowykonujące się funkcje anonimowe
W języku JavaScript bardzo często wykorzystuję się samowykonujące się funkcje anonimowe. Funkcja tworzona jest wówczas poprzez wyrażenie funkcyjne, a następnie zostaje natychmiastowo wykonana. Rozwiązanie to jest niezwykle przydatne jeśli nie chcesz zaśmiecać globalnej przestrzeni nazw swoim kodem — zmienne zadeklarowane wewnątrz funkcji nie są widoczne poza nią.
Przykład 2.37. Samowykonująca się funkcja anonimowa
(function(){
var foo = 'Witaj, świecie';
})();
console.log(foo); // niezdefiniowane!
Funkcja jako argument
W języku JavaScript, funkcje można określić mianem „obywateli pierwszej kategorii” — mogą być one przypisywane do zmiennych lub przekazywane jako argumenty innych funkcji. Przekazywanie funkcji jako argumentu to bardzo częsta praktyka w jQuery.
Przykład 2.38. Przekazywanie funkcji anonimowej jako argumentu
var mojaFn = function(fn) {
var rezultat = fn();
console.log(rezultat);
};
mojaFn(function() { return 'witaj, świecie'; }); // rejestruje witaj, świecie
Przykład 2.39. Przekazywanie nazwanej funkcji jako argumentu
var mojaFn = function(fn) {
var rezultat = fn();
console.log(rezultat);
};
var mojaDrugaFn = function() {
return 'witaj, świecie';
};
mojaFn(mojaDrugaFn); // rejestruje witaj, świecie
Sprawdzanie typu zmiennej
Język JavaScript pozwala na sprawdzenie „typu” danej zmiennej. Rezultaty mogą być jednak dezorientujące — przykładowo typ tablicy to obiekt (object
).
Do sprawdzania typu wartości często używany jest operator typeof
.
Przykład 2.40. Sprawdzanie typów różnych zmiennych
var mojaFunkcja = function() {
console.log('witaj');
};
var mojObiekt = {
foo : 'bar'
};
var mojaTablica = [ 'a', 'b', 'c' ];
var mojCiag = 'witaj';
var mojaLiczba = 3;
typeof mojaFunkcja; // zwraca function
typeof mójObiekt; // zwraca object
typeof mojaTablica; // zwraca object – uwaga!
typeof mójCiąg; // zwraca string
typeof mojaLiczba; // zwraca number
typeof null; // zwraca object – uwaga!
if (mojaTablica.push && mojaTablica.slice && mojaTablica.join) {
// prawdopodobnie jakaś tablica
// (jest to tak zwane „kacze typowanie”)
}
if (Object.prototype.toString.call(mojaTablica) === '[object Array]') {
// To na pewno tablica!
// Powszechnie uważa się to za najlepszy sposób
// do określenia, czy dana wartość jest tablicą
}
Biblioteka jQuery zawiera metody pomocnicze, które pozwalają ustalić typ dowolnej wartości. Metody te zostaną omówione w dalszej części kursu.
Słowo kluczowe this
W JavaScripcie, podobnie jak w większości języków programowania obiektowego, this
jest specjalnym słowem kluczowym wykorzystywanym w metodach w celu odwołania się do obiektu, na którym dana metoda jest wywoływana. Wartość this
określana jest w kilku prostych krokach:
- Jeśli funkcja zostaje wywołana metodą
Funkcja.call
lubFunkcja.apply
,this
przyjmie wartość pierwszego argumentu przekazanego do metodycall/apply
. Jeśli pierwszy argument przekazany do metodycall/apply
ma wartośćnull
lubundefined
,this
będzie odnosić się do obiektu globalnego (w przeglądarkach internetowych jest to obiektwindow
). - Jeśli wywoływana funkcja została utworzona przy pomocy metody
Funkcja.bind
,this
przyjmie wartość pierwszego argumentu, który został przekazany do metodybind
w czasie tworzenia funkcji. - Jeśli funkcja jest wywoływana jako metoda obiektu,
this
odwołuje się do tego obiektu. - W pozostałych przypadkach wywoływana jest samodzielna funkcja, niepowiązana z żadnym obiektem — wówczas
this
odwołuje się do obiektu globalnego.
Przykład 2.41. Funkcja wywołana przy użyciu metody Funkcja.call
var mojObiekt = {
przywitajSie : function() {
console.log('Cześć! Mam na imię ' + this.mojeImie);
},
mojeImie : 'Rebecca'
};
var drugiObiekt = {
mojeImie : 'Colin'
};
mojObiekt.przywitajSie(); // rejestruje Cześć! Mam na imię Rebecca
mojObiekt.przywitajSie.call(drugiObiekt); // rejestruje Cześć! Mam na imię Colin
Przykład 2.42: Funkcja utworzona przy pomocy metody Function.bind
var mojeImie = 'obiekt globalny',
przywitajSie = function () {
console.log('Cześć! Mam na imię ' + this.mojeImie);
},
mojObiekt = {
mojeImie : 'Rebecca'
};
var mojObiektCzesc = przywitajSie.bind(mojObiekt);
przywitajSie(); // rejestruje Cześć! Mam na imię obiekt globalny
mójObiektCześć(); // rejestruje Cześć! Mam na imię Rebecca
Przykład 2.43: Funkcja wiązana z obiektem w czasie działania programu
var mojeImie = 'obiekt globalny',
przywitajSie = function () {
console.log('Czesc! Mam na imie ' + this.mojeImie);
},
mojObiekt = {
mojeImie : 'Rebecca'
},
drugiObiekt = {
mojeImie : 'Colin'
};
mojObiekt.przywitajSie = przywitajSie;
drugiObiekt.przywitajSie = przywitajSie;
przywitajSie(); // rejestruje Cześć! Mam na imię obiekt globalny
mójObiekt.przywitajSie(); // rejestruje Cześć! Mam na imię Rebecca
drugiObiekt.przywitajSie(); // rejestruje Cześć! Mam na imię Colin
Zakres dostępności zmiennych JavaScript
„Zakres” odnosi się do zmiennych dostępnych w danym bloku kodu, w określonym czasie. Niezrozumienie pojęcia zakresu często sprawia, że męczymy się z uciążliwymi usterkami.
Zmienna zadeklarowana wewnątrz funkcji poprzez słowo kluczowe var
dostępna jest tylko w obrębie tej funkcji — nie można więc skorzystać z takiej zmiennej poza tą funkcją. Dostęp do tej zmiennej będą mieć natomiast funkcje zdefiniowane wewnątrz tej funkcji.
Ponadto zmienne zadeklarowane wewnątrz funkcji bez słowa kluczowego var
nie są dla danej funkcji zmiennymi lokalnymi — JavaScript przeglądnie cały łańcuch zakresu aż do zakresu okna by ustalić, gdzie zmienna ta została zdefiniowana. Jeśli nie znajdzie tej definicji, zmienna ta zostanie potraktowana jako globalna, co może mieć zupełnie nieprzewidywalne konsekwencje.
Przykład 2.44. Funkcje mają dostęp do zmiennych zdefiniowanych w obrębie tego samego zakresu
var foo = 'witaj';
var przywitajSie = function() {
console.log(foo);
};
przywitajSie(); // rejestruje witaj
console.log(foo); // również rejestruje witaj
Przykład 2.45. Kod znajdujący się poza zakresem, w którym została zdefiniowana zmienna, nie ma dostępu do tej zmiennej
var przywitajSie = function() {
var foo = 'witaj';
console.log(foo);
};
przywitajSie(); // rejestruje witaj
console.log(foo); // nic nie rejestruje
Przykład 2.46. Zmienne o takiej samej nazwie mogą znajdować się w różnych zakresach i przybierać różne wartości
var foo = 'świecie';
var przywitajSie = function() {
var foo = 'witaj';
console.log(foo);
};
przywitajSie(); // rejestruje witaj
console.log(foo); // rejestruje świecie
Przykład 2.47. Po zdefiniowaniu funkcja może „zobaczyć” zmiany w wartościach zmiennych
var mojaFunkcja = function() {
var foo = 'witaj';
var mojaFn = function() {
console.log(foo);
};
foo = 'świecie';
return mojaFn;
};
var f = mojaFunkcja();
f(); // rejestruje 'świecie' – ups
Przykład 2.48. Zakresy mogą nas często przyprawić o ból głowy
// samowykonująca się funkcja anonimowa
(function() {
var baz = 1;
var bim = function() { alert(baz); };
bar = function() { alert(baz); };
})();
console.log(baz); // baz nie jest zdefiniowany poza zakresem funkcji
bar(); // bar jest zdefiniowany poza zakresem funkcji anonimowej
// ponieważ nie został zadeklarowany przy pomocy słowa kluczowego var; ponadto
// ponieważ został zdefiniowany w tym samym zakresie co baz,
// bar ma dostęp do baz – zaś inny kod
// poza zakresem funkcji tego dostępu nie ma
bim(); // bim nie jest zdefiniowany poza zakresem funkcji anonimowej,
// w rezultacie wystąpi błąd
Zamknięcia (closures)
Zamknięcia stanowią rozszerzenie pojęcia zakresu — funkcje mają dostęp do zmiennych, które były dostępne w zakresie, w którym utworzono te funkcje. Jeśli definicja ta wydaje ci się niejasna, nie przejmuj się: zamknięcia najlepiej zrozumieć na przykładach.
Przykład 2.47 demonstrował w jaki sposób funkcje mają dostęp do zmienionych wartości zmiennych. Podobnie rzecz ma się w przypadku funkcji zdefiniowanych wewnątrz pętli — po zdefiniowaniu funkcja „widzi” zmianę wartości zmiennej i w rezultacie wszystkie kliknięcia wywołują okno alertu z komunikatem 5.
Przykład 2.49. Jak zamknąć wartość i
/* kod nie zachowa się tak, jakbyśmy chcieli; */
/* każde kliknięcie wywoła okno alertu z komunikatem 5 */
for (var i=0; i<5; i++) {
$('<p>kliknij mnie</p>').appendTo('body').click(function() {
alert(i);
});
}
Przykład 2.50. „Zamykamy” wartość i poprzez zamknięcie
/* rozwiązanie: zamykamy wartość i wewnątrz funkcji utwórzFunkcje, aby wartość tej zmiennej nie uległa zmianie */
var utworzFunkcje = function(i) {
return function() { alert(i); };
};
for (var i=0; i<5; i++) {
$('<p>kliknij mnie</p>').appendTo('body').click(utworzFunkcję(i));
}
Zamknięcia mogą służyć również do rozwiązywania problemów ze słowem kluczowym this
, które dla każdego zakresu jest unikalne:
Przykład 2.51. Jak uzyskać jednoczesny dostęp do wewnętrznego i zewnętrznego obiektu używając domknięcia
var zewnetrznyObj = {
mojaNazwa : 'zewnętrzny',
zewnetrznaFunkcja : function () {
// odniesienie do obiektu zewnetrznyObj jest zamknięte w funkcji wewnetrznaFunkcja
var self = this;
var wewnetrznyObj = {
mojaNazwa : 'wewnętrzny',
wewnetrznaFunkcja : function () {
console.log(self.mojaNazwa this.mojaNazwa); // rejestruje 'zewnętrzny wewnętrzny'
}
};
wewnetrznyObj.wewnetrznaFunkcja();
console.log(this.mojaNazwa); // rejestruje 'zewnętrzny'
}
};
zewnętrznyObj.zewnetrznaFunkcja();
dzięki za artykuł, z tym ze przez błędy w prostych przykładach trochę to słabo wszystko wypada.
Bo wydaje się ze rozumiem to co wyjaśniasz a w przykładzie wygląda to inaczej niż powinno w stosunku do tego co sam tłumaczysz.
Jako poczatkujacy javascriptowiec moge powiedziec ze w co 2 przykladzie sa bledy. Prosze popraw to. Bo wprowadzaja w blad osobe uczaca sie