Dla osób rozpoczynających swoją przygodę z językiem JavaScript jedno z trudniejszych zagadnień stanowią domknięcia. W tym artykule postaram się przybliżyć ten ciekawy temat odpowiadając na pytania czym są domknięcia oraz przedstawiając przykłady ich użycia w kodzie.
Definicji domknięć w JavaScript jest wiele. Głównie sprowadzają się do tego, że jest to wydzielony obszar stworzony przez główną funkcję, w której wszystkie zmienne i wewnętrzne funkcje są niezależne od pozostałej części kodu. Zaletą domknięć jest to, że wewnętrzne funkcje mają dostęp zewnętrznych funkcji, natomiast zewnętrzne funkcje nie mają dostępu do wewnętrznych.
Temat z definicji dosyć prosty ale wciąż nie do końca rozumiany.
Najlepiej zobrazuje to prosty przykład:
var myName = ‘Marcin’;
var myAge = 31;
function changeData(){
var myName = ‘Piotrek’;
myAge = 32;
}
changeData();
console.log(myName);
console.log(myAge);
W powyższym wypadku do konsoli wydrukujemy odpowiednio Marcin i 32. Zmienna myName wewnątrz funkcji changeData jest zmienną do której dostęp ma tylko funkcja myName.
Przeanalizujmy inny przykład:
function buildName(name){
var greeting = “Hello, ” + name;
return greeting;
}
Funkcja buildName() deklaruje zmienną lokalną greeting i zwraca ją. Każde wywołanie funkcji tworzy nowy zakres z nową zmienną lokalną i po wykonaniu tej funkcji, nie mamy możliwości ponownego odniesienia się do tego zakresu.
Z pomocą przychodzą nam domknięcia:
function myName(name){
var greeting = "Cześć, " + name;
var sayName = function(){
var welcome = greeting + " Pozdrawiam!";
console.log(welcome);
};
return sayName;
}
var sayMyName = myName("Marcin");
sayMyName(); // Cześć, Marcin Pozdrawiam!
sayMyName(); // Cześć, Marcin Pozdrawiam!
sayMyName(); // Cześć, Marcin Pozdrawiam!
Funkcja sayName() z tego przykładu jest domknięciem.
Funkcja sayName() ma własny zasięg lokalny (ze zmienną welcome) i ma również dostęp do zakresu funkcji zewnętrznej. W tym przypadku zmienna greeting z funkcji myName().
Normalnie po wykonaniu funkcji zakres jest niszczony i nie ma do niego dostępu. Po wykonaniu funkcji myName() zakres nie zostanie w tym przypadku zniszczony. Funkcja sayMyName() nadal ma do niego dostęp.
Domknięcie służy jako brama między globalnym kontekstem a zewnętrznym zakresem.
Na koniec chciałbym przedstawić przykład bardzo często poruszany na rozmowach kwalifikacyjnych, a mianowicie słynne setTimeout w pętli.
for(var i = 0; i < 5; i++){
setTimeout(function(){
console.log(i);
}, 300);
}
Co spowoduje ten kod? Pierwszą myślą jest wydrukowanie do konsoli cyfr od 0 do 4. Jest to częsty błąd. Kod ten wydrukuje pięć razy piątkę.
Dlaczego tak się dzieje? Zmienna i jest w przy wypadku zmienną globalną i za każdym razem przekazujemy tą samą wartość zmiennej i.
Jak uzyskać wynik od 0 do 4?
Możemy skorzystać z domknięć, a dokładniej z funkcji IIFE (Immediately-Invoked Function Expression). Przykład ten będzie wyglądał tak:
for (var i = 0; i < 5; i++){
(function (e){
setTimeout(function (){
console.log(e);
}, 300);
})(i);
}
Dlaczego teraz osiągniemy zamierzony efekt? Ponieważ funkcja anonimowa ma swój własny zakres i za każdym razem przekazujemy do niej inną wartość zmiennej i.
Przy pierwszym przebiegu pętli, wywołujemy od razu IIFE z parametrem i o aktualnej wartości 0 (wartość z domknięcia). Wywołanie IIFE powoduje ustawienie instrukcji console.log do wykonania z 300ms opóźnieniem z wartością zmiennej e równej 0. Rozpoczyna się nowe przejście przez pętlę. Tym razem zmienna i ma wartość 1 i zostaje przekazana do IIFE, która ustawia wykonanie console.log za 300ms ze zmienną e, o wartości 1 i tak aż do 4.
Podsumowując:
Domknięć możemy użyć w sytuacjach w których:
- Chcemy emulować enkapsulację metod
- Normalnie użylibyśmy obiektu z wyłącznie jedną metodą

Follow