Wypróbuj

Nozbe - get things done

i zwiększ swoją produktywność

Domknięcie JavaScript – proste problemy z this

3

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:

  1. W momencie tworzenia zmiennej module wywoływana jest funkcja natychmiastowa, która zwraca obiekt zawierający dwie właściwości: index oraz doSmth. Do właściwości tych odpowiednio kopiowana jest wartość początkowa zmiennej prywatnej index oraz kopiowana jest referencja do funkcji doSmth (to nie są tak na prawdę ta sama zmienna i funkcja)
  2. Wywołanie module.doSmth() powoduje zmianę lokalnej zmiennej index – właściwość index zwróconego obiektu (tego który siedzi teraz w zmiennej module) pozostaje nie naruszona
  3. 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łanie module.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 😉

CHCESZ DARMOWEGO E-BOOKA?

Jeśli chcesz otrzymać mojego e-booka: Rozmowa Kwalifikacyjna - pytania z podstaw JavaScript zostaw mi swój e-mail:

Oprócz tego co poniedziałek dostaniesz maila z listą moich wpisów z poprzedniego tygodnia!

  • Grzegorz

    Lepiej nie używać thisa. Utrudnia tylko refaktoryzację kodu. Lepiej to zrobić tak :

    var module = (function () {

    var index = 0;

    return {
    getIndex: getIndex,
    doSmth: doSmth
    };

    function getIndex() {
    return index;
    }

    function doSmth() {

    index = 1;
    alert(index);
    }

    }());

    • Bartłomiej Dybowski

      Oczywiście, dokładnie tak jak napisałem na samym końcu wpisu… Zachęcam do czytania postów do końca przed napisaniem komentarza 😀

  • Grzegorz

    Życia szkoda na czytanie wszystkiego :p