Immediately-invoked Function Expression IIFE – jak dokładnie działa
Chyba każdy, kto pracował z JavaScriptem widział taki zapis:
(function() { ... })() // lub (() => { ... })()
Immediately-invoked Function Expression (IIFE)
Co więcej, pewnie większość wie do czego taki zapis służy i pewnie większość takiego zapisu używało. Jest to tak zwane Immediately-invoked Function Expression (IIFE), czyli funkcja, która od razu się wywołuje.
Pytanie, ile osób wie dlaczego ten zapis wygląda właśnie tak? Dlaczego potrzebnych jest tyle nawiasów? Po co go stosować? Dzisiaj postaram się odpowiedzieć na wszystkie te pytania.
Czym jest IIFE?
Jak sama nazwa mówi jest to natychmiastowo samowywołująca się funkcja. Czyli funkcja, która wykonuje się od razu. I tyle, nie ma co komplikować.
Dlaczego stosować IIFE a nie zwykłą funkcję?
Skoro jest to funkcja, to dlaczego utrudniamy sobie ten zapis? Przecież coś takiego dałoby się zapisać, np. w taki sposób:
function start() { ... } start()
Zadziałałoby w ten sam sposób i jest czytelniej, prawda? Teoretycznie tak. Zastanówmy się w takim razie, po co w ogóle używać funkcji zamiast napisać kod od razu, omijając funkcję.
Po co w ogóle pisać kod w funkcji?
Powodów jest kilka:
- jeśli mamy kod w funkcji, wiadome jest, że jest on jakoś ze sobą powiązany, że jest to część tego samego skryptu
- kod jest czytelniejszy
- NAJWAŻNIEJSZE: nie zaśmiecamy globalnej przestrzeni nazw. Nazwy zmiennych/funkcji naszego kodu nie będą kolidowały z innymi skryptami, które mogą używać takich samych nazw. Nasz kod jest hermetycznie zamknięty. Tak jakby miał swoje własne środowisko!
Skoro wrzucanie kodu do funkcji ma sens, to dlaczego nie użyć zwykłej funkcji zamiast IIFE?
Dlaczego akurat IIFE?
W przypadku zwykłej funkcji tworzymy jej nazwę a to już prowadzi do potencjalnej kolizji z innymi skryptami. Bo przecież ta nazwa znajdzie się w globalnej przestrzeni, więc jakiś inny skrypt będzie miał taką samą funkcję cała nasza aplikacja przestanie działać.
W przypadku IIFE jest to funkcja anonimowa – nie ma żadnej nazwy. Skoro nie ma nazwy – nie ma mowy o ewentualnej kolizji.
Kolejny plus to zapis, który na początku wydaje się dziwny. Jeśli się lepiej przyjrzeć to takie zapis
(() => { ...kod... })()
jest czytelniejszy, bezpieczniejszy i szyby do napisania niż
function someFunction() { ...kod... }
someFunction()
Należy tylko zrozumieć dlaczego ten zapis wygląda tak jak wygląda
Co oznacza (() => { … })(). Dlaczego tyle nawiasów?
Powyższy zapis można rozbić na 2 części
- () => {} – zwykła funkcja strzałkowa, równie dobrze to może być tradycyjna funkcja czyli function() {}
- () – nawias na końcu, czyli uruchomienie funkcji
Okej, wg tego kod wyglądałby tak: () => {}() czyli utworzenie funkcji i od razu jej wywołanie. To sam sens bo przecież IIFE to (anonimowa) funkcja, która od razu się wywołuje.
Jednak wciąż brakuje nawiasu do okoła funkcji.
Błąd parsera
Po próbie wywołania takiego kodu () => {}() zobaczymy błąd:
Uncaught SyntaxError: Unexpected token '(’
Początek jest dobry, można zdefiniować funckję i do niczego jej nie przypisać, ale JS nie spodziewa się ostatniego nawiasu. Gramatycznie to jest niepoprawne i tyle.
Trzeba więc to jakoś obejść…
Wyrażenie to nie funkcja
Dodając do siebie liczby używamy często nawiasów, aby zmienić kolejność działań, np. (2 + 4) * 2
JS traktuje wartość w nawiasach jako wyrażenie. Wykonuje operacje, które są w środku i zwraca wartość (w tym przypadku doda liczby i zwróci 6). Takie wyrażenie może znajdować się w dowolnym miejscu aplikacji. Parser chyba nigdy nie zwróci nam błędu, że nie spodziewa się wyrażenia.
Co jeśli w nawiasie będzie funkcja?
(function() { … }) // lub (() => { … })
Okazuje się, że JS potraktuje tę funkcję jako wyrażenie. Taki zapis jest całkowicie poprawny. Dodajmy teraz do tego kolejny nawias – czyli uruchomienie funkcji.
(function() { ... })()
// lub
(() => { ... })()
Okazuje się, że JS trzyma w pamięci naszą funkcję, ale traktuje ją jak wyrażenie (z racji, że było w nawiasie) a następie dostawia ostatni nawias do wyrażenia – dzięki czemu nasza funkcja się uruchamia!
Podsumowując jest to tak jakby obejście parsera. Dzięki któremu możemy uruchomić antychmiastowo anonimową funkcję, której normalnie byśmy uruchomić nie mogli.