
Hej, dziś wpis trochę krótszy, z rodzaju tych “tips & tricks”… Czasem zdarza nam się natknąć na sytuację kiedy napisaliśmy w pocie czoła jakiś “zajebisty”, “zakręcony” kod JavaScript ale nie wiedzieć czemu nie działa - dzieje się tak często dlatego, że JavaScript w wielu sytuacjach zachowuje się inaczej od innych topowych języków programowania. Dziś we wpisie jedna z takich sytuacji. Domknięcie JavaScript to jedno z podstawowych zagadnień w tym języku. Czasem jednak może ono nam przysporzyć problemów…

Domknięcie JavaScript - proste problemy z this
Hej, dziś wpis trochę krótszy, z rodzaju tych “tips & tricks”… Czasem zdarza nam się natknąć na sytuację kiedy napisaliśmy w pocie czoła jakiś “zajebisty”, “zakręcony” kod JavaScript ale nie wiedzieć czemu nie działa - dzieje się tak często dlatego, że JavaScript w wielu sytuacjach zachowuje się inaczej od innych topowych języków programowania. Dziś we wpisie jedna z takich sytuacji. Domknięcie JavaScript to jedno z podstawowych zagadnień w tym języku. Czasem jednak może ono nam przysporzyć problemów…
Domknięcie JavaScript - studium przypadku
No skoro studium przypadku, to zobaczmy ten przypadek…
var module = (function () {
var
index = 0,
doSmth = function () {
index = 1;
alert(index);
};
return {
doSmth: doSmth
}
}());
module.doSmth();
Jak widzicie przykład bardzo prosty - moduł, zawierający zmienną prywatną index
, oraz funkcję doSmth
. Funkcja ta wykorzystuje domknięcie JavaScript (ang. closure), a więc ma swobodny dostęp do zmiennej index
, dlatego może dokonać jej modyfikacji i wyświetlić jej zawartość za pomocą polecenia alert
.
Problemy…
Ok, to teraz do kodu siada inny programista i stwierdza, że potrzebuje mieć dostęp do zmiennej index na zewnątrz modułu:
var module = (function () {
var
index = 0,
doSmth = function () {
index = 1;
alert(index);
};
return {
index: index,
doSmth: doSmth
}
}());
module.doSmth();
alert(module.index);
No świetnie, tylko że to nie zadziała tak jak on się tego spodziewa… Powyższy kod wyświetli najpierw wartość 1
, a następnie wartość 0
(tutaj jsfiddle jeśli ktoś chce sobie kliknąć) Pytanie tylko dlaczego 0
a nie 1
skoro wcześniej wywołano funkcję, która teoretycznie powinna zmienić już wartość zmiennej index
.
Rozwiązanie
No to spójrzmy co się tutaj dzieje:
- W momencie tworzenia zmiennej
module
wywoływana jest funkcja natychmiastowa, która zwraca obiekt zawierający dwie właściwości:index
orazdoSmth
. Do właściwości tych odpowiednio kopiowana jest wartość początkowa zmiennej prywatnejindex
oraz kopiowana jest referencja do funkcjidoSmth
(to nie są tak na prawdę ta sama zmienna i funkcja) - Wywołanie
module.doSmth()
powoduje zmianę lokalnej zmiennejindex
- właściwośćindex
zwróconego obiektu (tego który siedzi teraz w zmiennejmodule
) pozostaje nie naruszona - Z kolei wywołanie
module.index
zwraca wartość właściwości zwróconego na początku obiektu - ta wartość nie została zmieniona przez wywołaniemodule.doSmth()
Ok, wszystko jasne… Rozwiązaniem mogłoby być użycie this
w funkcji doSmth
:
var module = (function () {
var
index = 0,
doSmth = function () {
this.index = 1;
alert(this.index);
};
return {
index: index,
doSmth: doSmth
}
}());
module.doSmth();
alert(module.index);
No teraz działa, w obu przypadka wyświetla się wartość 1
. Tylko, że to nie do końca o to chodzi ponieważ teraz nie dotykamy w ogóle zmiennej lokalnej index
funkcji natychmiastowej, można by ją całkiem wywalić i inicjować ją jakąś wartością podczas tworzenia obiektu zwracanego przez tę funkcję. Dlatego właśnie, nie powinno się tego robić w ten sposób - zamiast upubliczniać zmienną prywatną, tak jak to zrobił nasz przykładowy, wyimaginowany programista należałoby raczej zastosować funkcję pobierającą oraz funkcję ustawiającą (getter i setter)… I tym akcentem zakończę swój wywód ;)