Pierwsze zetknięcie z IIFE zaowocowało... głupawką. ;) Instytut Idyllicznej Fascynacji Entropią? A może: Impulsywna Idiosynkrazja Fantomowych Eskimosów. Albo: Immanentny Infantylizm Filuternych Ekscentryków. Lub...
No dobrze, wystarczy już tych propozycji w spontanicznym wyzwaniu pt. "Rozwiń skrót IIFE w idiotyczny sposób, używając mądrze brzmiących słów". ;) Teraz do rzeczy.
No dobrze, wystarczy już tych propozycji w spontanicznym wyzwaniu pt. "Rozwiń skrót IIFE w idiotyczny sposób, używając mądrze brzmiących słów". ;) Teraz do rzeczy.
IIFE, czyli Immediately-Invoked Function Expression. Cóż to takiego i do czego to?
Jak nazwa wskazuje, jest to wyrażenie funkcyjne wywoływane natychmiastowo, w skrócie: funkcja natychmiastowa. Jest ona wykonywana synchronicznie (tzn. od razu) i tylko jeden raz (nie można wywołać jej wielokrotnie, jak "zwykłych" funkcji). Na poziomie zapisu – to funkcja (anonimowa lub z nazwą) od razu wywołana i ujęta – wraz z wywołaniem – w nawiasy okrągłe:
(function() { ... }());
IIFE ma zastosowanie wtedy, gdy chcemy zabezpieczyć zmienną/zmienne przed "wyciekiem" do zakresu globalnego (global scope). Wewnątrz IIFE można tworzyć (a w zasadzie: emulować) prywatne zmienne. Precyzując: jest to możliwe, gdy IIFE przyjmuje postać domknięcia (closure); jest to specjalny sposób na stworzenie prywatnych zmiennych, bo tego rodzaju zmienne explicite w JS nie istnieją. Ale po kolei.
Zakresy zmiennych i domknięcia (closures)
Zakres (scope) zmiennych w JS jest ograniczony do funkcji. Zmienna zadeklarowana w ramach funkcji to zmienna lokalna – dostępna w danej funkcji i niszczona po jej wywołaniu. Zmienne lokalne pozostają niewidoczne w kodzie "na zewnątrz" funkcji, są jednak dostępne w funkcjach zagnieżdżonych (wewnętrznych).
Jeżeli wynikiem jednej funkcji jest druga funkcja (innymi słowy: gdy funkcja zwraca funkcję), mamy do czynienia ze wspomnianym domknięciem, np.:
Jeżeli wynikiem jednej funkcji jest druga funkcja (innymi słowy: gdy funkcja zwraca funkcję), mamy do czynienia ze wspomnianym domknięciem, np.:
function rodzic() { var x = 1; return function dziecko() { var y = 2; console.log(x + y); } }
Powtórzmy: funkcja zagnieżdżona (funkcja-dziecko) ma dostęp do własnych zmiennych oraz do zmiennych, które zostały zdefiniowane w funkcji zewnętrznej (funkcji-rodzicu). Co istotne: nawet po wywołaniu funkcji-rodzica, funkcja-dziecko zachowuje dostęp do zmiennych funkcji-rodzica – na tym polega istota domknięć. Rozkładając to na czynniki pierwsze:
- funkcja-rodzic jest wykonywana – jej wynikiem jest zwrócenie (
return
) funkcji-dziecka – i zostaje zakończona - oznacza to, że kontekst jej wywołania (czyli jej wszystkie zmienne) powinien zostać zniszczony
- tak się jednak nie dzieje, bo funkcja-dziecko (będąca wynikiem wykonania funkcji-rodzica) musi mieć dostęp do zmiennych funkcji-rodzica
- z tego właśnie powodu zakres funkcji-rodzica nie może zostać zniszczony (bo w takim przypadku - UWAGA: makabryczna metafora – ucięlibyśmy funkcji-dziecku np. rączki)
- wszystkie lokalne zmienne funkcji-rodzica są więc domykane (stąd nazwa "domknięcie"), czyli niejako "chwytane" przez zakres funkcji-dziecka. Niemakabryczna metafora: po wykonaniu i zakończeniu funkcji-rodzica jej scope żyje dla funkcji-dziecka. Tylko i wyłącznie dla niej.
- zmienne funkcji-rodzica pozostają niewidoczne dla wszystkich innych zakresów, w szczególności – dla globalnego.
Innymi słowy: zmienne funkcji-rodzica mogą stać się dostępne w global scope lub local scope innych funkcji (innych niż funkcja-dziecko) tylko pośrednio, tj.: poprzez wywołanie funkcji-dziecka, która ma dostęp do zmiennych funkcji-rodzica. I to właśnie znaczy że zmienna jest prywatna (po zakończeniu funkcji-rodzica "należy" bezpośrednio tylko do funkcji-dziecka, do nikogo, a raczej niczego, więcej).
Dla zmęczonych tym wywodem jeszcze jedna metafora, ujmująca, jak mi się wydaje, całość zagadnienia:
Funkcja-dziecko dziedziczy po "zmarłej" funkcji-rodzicu dostęp do jej zmiennych – i od tego momentu tylko i wyłącznie ona może z nich korzystać.
Na domknięcie rozważań o domknięciach i IIFE warto dodać, że IIFE może (i często jest), ale nie musi być, domknięciem. Jak w przykładzie poniżej:
Funkcja-dziecko dziedziczy po "zmarłej" funkcji-rodzicu dostęp do jej zmiennych – i od tego momentu tylko i wyłącznie ona może z nich korzystać.
Na domknięcie rozważań o domknięciach i IIFE warto dodać, że IIFE może (i często jest), ale nie musi być, domknięciem. Jak w przykładzie poniżej:
(function() { console.log(100); }());
A wracając jeszcze do zmiennych... Z posta o IIFE zrobił się post o zakresach zmiennych i domknięciach, zgodnie z holistyczną koncepcją świata. ;) Zatem wracając:
Zmienna niezawarta w funkcji to zmienna globalna. Znajduje się ona w przestrzeni globalnej, co oznacza, że jest dostępna i może być modyfikowana w całym programie. Uwaga na marginesie: niejawne zadeklarowanie zmiennej wewnątrz funkcji (czyli bez użycia słowa kluczowego
Zmienna niezawarta w funkcji to zmienna globalna. Znajduje się ona w przestrzeni globalnej, co oznacza, że jest dostępna i może być modyfikowana w całym programie. Uwaga na marginesie: niejawne zadeklarowanie zmiennej wewnątrz funkcji (czyli bez użycia słowa kluczowego
var
), np.:function() { x = 2; ... }
skutkuje utworzeniem zmiennej globalnej. Taki psikus.
Tyyyyyyyyle teorii. ;)
Zastosowanie IIFE w praktyce też już było (można zobaczyć tutaj), ale to więcej niż pewne, że będę to wszystko jeszcze długo "mielić". Cóż... Clark Kent też nie od razu odkrył w sobie Supermana. ;)
PS
Z Wikipedii dowiedziałam się jeszcze, że zapis IIFE, który podałam na początku tego posta, to tzw. Douglas Crockford's style. (I że pan Douglas to ważna persona w światku JS. ;)) A także, że IIFE można zapisywać na wiele sposobów, np. owijając tylko funkcję (bez jej wywołania) w nawiasy ( ):
Albo w ogóle bez nawiasów, z dodatkowym znakiem (
Z Wikipedii dowiedziałam się jeszcze, że zapis IIFE, który podałam na początku tego posta, to tzw. Douglas Crockford's style. (I że pan Douglas to ważna persona w światku JS. ;)) A także, że IIFE można zapisywać na wiele sposobów, np. owijając tylko funkcję (bez jej wywołania) w nawiasy ( ):
(function() { ... })();
Albo w ogóle bez nawiasów, z dodatkowym znakiem (
!
, ~
, -
, +
) przed function
.
Brak komentarzy:
Prześlij komentarz