Praktický JavaScript (2.) - IIFE (Immediately-invoked function expression)

Publikoval Michal Kočí dňa 30.07.2016 o 21:22 v kategórii JavaScript

Šanca je, že ste o IIFE (Immediately-invoked function expression) už počuli a pravidelne ich používate. Pokiaľ vám však IIFE nič nevraví, podťe sa pozrieť aký jednoduchý no účinný návrhový vzor sa za touto skratkou skrýva a kedy vám môže prísť vhod.

IIFE, Immediately-invoked function expression

IIFE by sa dalo preložiť ako okamžite použitý funkčný výraz (či výraz vracajúci funkciu) a v skutočnosti sa o nič komplikované nejedná, než o okamžite spustenú funkciu.

Expression (Výraz)

Poďme ale pekne po poriadku. Čo je vlastne expression (výraz)? Výraz je každá valídna časť programového kódu, ktorá vracia hodnotu. Tu je niekoľko príkladov výrazov v JavaScripte:

// aj keď sa jedná o priradenie, ide o výraz, ktorý vracia číselnú hodnotu 3
x = 3
// nasledujúci výraz vracia číselnú hodnotu 18
7 + 11
// nasledujúci výraz vracia textovú hodnotu Peter
"Peter"
// nasledujúci výraz vracia číselnú hodnotu 6
add(3, x)

Function (funkcia) a Function Expression (výraz vracajúci funkciu)

Poďme sa teraz pozrieť na funkcie. Toto by bola anonymná funkcia, ktorá neprijíma žiadne parametre, nič nerobí a nič nevracia:

function() { }

Teda, v skutočnosti aj funkcia, ktorá nemá v sebe žiaden return niečo vracia - vracia hodnotu undefined. To len mimochodom, ak by vás to zaujímalo.

No a keď predchádzajúci výraz obalíme zátvorkami, stane sa z anonymnej funkcie výraz. A nie zrovna obyčajný - je to výraz, ktorý vracia funkciu. Áno, výsledkom tohto výrazu je funkcia:

(function() { })

Zaujímavé, ale stále nie veľmi praktické, však? Vravíte si, dobre, ale na čo mi je výraz, ktorý mi vracia funkciu? Čo s ním? Vydržte, trochu teraz naoko odbočím...

Spustenie (zavolanie) funkcie

Predstavte si, že do premennej add priradíte anonymnú funkciu, táto prijíma dve čísla a vracia ich súčet. A chcete ju použiť na sčítanie trojky a štvorky a výsledok si uložiť do premennej x. Alebo viete čo? Nepredstavujte si to, radšej si to tu pozrite:

var add = function (a, b) {
  return a + b;
}
var x = add(3, 4);

Čo vidíte v predchádzajúcom kóde? Máte tam funkciu priradenú do premennej add. A tú spúšťate a výsledok jej volania si ukladáte do premennej x.

V podstate je add výrazom vracajúcim funkciu. A každú funkciu alebo výraz vracajúci funkciu vieme spustiť jednoducho tak, že za nimi uvedieme zátvorky (a do nich prípadne vložíme parametre).

Okamžite spustená funkcia

Odpustite drobné odbočenie, bolo však podstatné. Totiž, videli sme výraz vracajúci funkciu, ktorý sme v zápätí spustili. Zrejme ho chceme mať v premennej (alebo sme si mohli funkciu pomenovať a nepriradzovať do premennej), lebo je pravdepodobné, že je užitočná a budeme ju chcieť spustiť viac krát.

Lenže, nie vždy potrebujeme funkciu spustiť viac krát, niekedy nám stačí práve jedno jej zavolanie. Spomeňte si na náš prvý príklad výrazu vracajúceho funkciu:

(function() { })

Ako by sme mohli tento výraz, túto funkciu spustiť? Veľmi jednoducho, dáme za ňu zátvorky:

(function() { })()

A je to. Naša prvá, síce absolútne neužitočná, ale prvá IIFE.

Len pre zábavu, skúsme si na IIFE prepísať add, išlo by to? Išlo:

var x = (function(a, b) { return a + b; })(3, 4);

Tento príklad je dôležitý, nakoľko na ňom vidno, že takýto výraz:

  • je hneď spustený; do x sa nám priradí hodnota súčtu (7)
  • môže prijímať parametre; tu prijíma parametre pomenované a a b
  • výsledok výrazu si môžeme priradiť do premennej, ďalej s ním pracovať a pod.

Praktické použitie IIFE

Ukážeme si jedno praktické použitie a k ďalším sa dostaneme v nasledujúcich príspevkoch, verte mi.

Problém: Kolízia názvu externých knižníc

jQuery. Najprv milované, potom mnohými zatracované. Ale ako príklad poslúži dokonale. Ak ste s ním nikdy nepracovali, vedzte, že bolo dostupné na globálnom scope (t.j. na objekte window) pod dvomi názvami: jQuery a $. Krásne pomenovanie, ten dolár, však? Krátke a teda praktické a programátormi preferované.

Lenže, jQuery nebolo jedinou knižnicou, ktorá sa rozhodla používať dolár. Takže ak ste načítali inú knižnicu, ktorá tiež používala dolár, mohli ste nepekne naraziť na kolíziu názvov. Čo bude zrazu dostupné pod dolárom? jQuery? Iná knižnica? Kto vie...

A teraz si predstavte, že váš web obsahoval niekoľko (niekedy desiatky) skriptov, ktoré jQuery používali. A keďže sme tvory lenivé, nikomu sa nechce používať dlhý názov (jQuery), takže každý používa skrátený názov. A zrazu ste si načítali inú knižnicu a problém bol na svete.

Riešenie: IIFE

Riešenie je jednoduchšie, než by sa mohlo zdať - použiť IIFE. Ale to ste zrejme čakali, že? Tak sa radšej poďme pozrieť, ako na to.

Pôvodná verzia skriptu

Predstavte si, že toto je obsah jedného z vašich mnohých skriptov, za úlohu má niečo úžasné schovať pri načítaní stránky:

$(document).ready(function() {
  $('.hide-after-load').hide();
});

Nič úžasné a ani moc reálne, ale ako príklad nám to postačí. Ak takýto skript načítate a pod dolárom sa v skutočnosti nenachádza jQuery, čo sa stane? Ťažko povedať, možno skript zlyhá, možno spraví niečo úplne iné než čakáte, záleží na tom, čo sa pod dolárom reálne ukrýva.

Upravená a opravená verzia skriptu

Stačí nám však spojiť dve znalosti a máme vystarané: IIFE a fakt, že jQuery je dostupné aj ako jQuery. Potom stačí celý náš skript obaliť do IIFE a do nej poslať jeden parameter (jQuery), ktorý si však funkcia pomenuje ako $. A je to:

(function($) {
  $(document).ready(function() {
    $('.hide-after-load').hide();
  }
})(jQuery);

Krátka rekapitulácia tohto kódu. Máme tu IIFE, teda funkciu, ktorá je okamžite spustená. Na funkčnosti skriptu sa teda nič nemení, aj doteraz bol jeho obsah okamžite spustený.

Naša IIFE prijíma práve jeden parameter a ten je pomenovaný ako $. Tým pádom vnútri tejto IIFE je pod dolárom to, čo do nej pošleme ako hodnotu prvého parametra.

Ak tam pošleme jQuery (čo aj robíme), ako dolár je dostupný tento framework a tým pádom sa nám absolútne nezmenila ani funkčnosť skriptu. A je jedno či na globálnom scope (window) je pod dolárom dostupné niečo iné. Kód vnútri pod dolárom vidí práve jQuery.

Problém: Kolízia názvu premenných

Že jQuery nepoužívate? To absolútne nevadí. Možno používate inú knižnicu a chcete sa brániť pred kolíziou názvov.

Ovšem, pri použití viacerých skriptov môžeme naraziť na ďalší problém kolízie.

Predstavte si tieto dva skripty, zase v nich síce použijem jQuery, ale tentoraz je pointa inde. Toto je prvý skript, po sekunde po načítaní má schovať niektoré elementy:

var elements = $('.should-be-hidden');
setTimeout(function() {
  elements.hide();
}, 1000);

A toto je druhý skript, ktorý má po kliknutí kdekoľvek v dokumente zobraziť nejaké iné elementy:

var elements = $('.show-after-click');
$(document).click(function() {
  elements.show();
});

Verím, že ste hneď zbadali problém týchto skriptov. Áno, je to kolízia názvu premennej elements. Jeden skript si do nej priraďuje nejaké elementy, druhý iné. Nastáva kolízia názvu premenných.

Nie je teraz dôležité, že elementy ste si nemuseli ukladať do premennej, ale mohli ste si ich načítať až keď ich budete reálne potrebovať. Rovnako nie je dobrá odpoveď v zmysle, že by ste použili lepšie názvy premenných tak, aby nekolidovali. Toto problém totiž nerieši globálne.

Ak budete jednotlivé skripty písať takto, kedykoľvek v budúcnosti vám môže prísť ku kolízii názvov a vec sa má tak, že na takýto typ problémov sa veľmi ťažko prichádza. Často ich objavia až vaši používatelia a aj to ho nebudú samozrejme vedieť správne pomenovať, len im niečo nebude fungovať a nájsť príčinu bude na vás.

Ešte sa vráťme k tomu, prečo v tomto príklade dochádza ku kolízii názvov. Každá premenná má v JavaScripte scope, na ktorom je definovaná a v ktorom je prístupná. A jedno z pravidiel znie, že premenná, ktorá je síce deklarovaná použitím kľúčového slova var ale je definovaná mimo funkciu sa priradí do globálneho scopu. Do globálneho. Na objekt window.

Ak teda máte sériu skriptov a v nich deklarované premenné mimo funkcie, tie premenné budú na objekte window. Ako v našom prípade. Kolízia názvov ako vyšitá.

Riešenie? Opäť IIFE

Ak opäť použijete v skriptoch IIFE, situácia sa rapídne zmení. Tu je upravený prvý skript:

(function($) {
  var elements = $('.should-be-hidden');
  setTimeout(function() {
    elements.hide();
  }, 1000);
})(jQuery);

A tu druhý:

(function($) {
  var elements = $('.show-after-click');
  $(document).click(function() {
    elements.show();
  });
})(jQuery);

Okrem toho, že sa opäť bránime proti kolízii jQuery cez dolár, premenné elements v oboch skriptoch sú teraz deklarované stále cez var, ale sú deklarované v rámci funkcie. A teda, ich scope je v tejto funkcii, nie sú dostupné na globálnom objekte a nemôže tak prísť ku kolízii ich názvov. Takže sme použitím IIFE získali ďalšiu ochranu proti prípadným problémom.

Ponaučenie a záver

Takže, ak ešte IIFE, snáď som vás presvedčil, že jeho použitie sa oplatí a pomôže vám vyvarovať sa kolízii názvov. A hlavne, nejedná sa o nič komplikované a ani o nič magické.

Ak si zvyknete skripty písať hneď tak, že ako prvé napíšete IIFE, bude váš programátorský život o niečo krajší, verte mi.

Ak vás zaujímajú ďalšie vhodné scenáre, sledujte ma ďalej, k niektorým sa v rámci tohto seriálu dostaneme. Ale ak ste netrpezlivý a chcete aspoň zhruba vedieť, kde ešte vám IIFE pomôžu, tak napríklad pri problémoch pri iterácii a prístupe ku closure v rámci iterácie. Ale o tom naozaj až nabudúce...

Chcete sa poriadne naučiť JavaScript a ešte sa pritom naučiť nejaký framework či knižnicu (napríklad Angular či React)? Prídite na niektoré z mojich JavaScriptových školení a naučte sa to rýchlo a jednoducho.

Mohlo by ťa tiež zaujímať

Páčil sa ti príspevok?

Zdieľaj príspevok alebo si ho odlož na neskôr

Sleduj ma

Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.

Komentáre

K tomuto článku nie su pridané žiadne komentáre.

Pridať komentár

Máš niečo zaujímavé povedať k článku? Pridaj to k článku ako komentár. Spam, reklamu alebo inak nerelevantné komentáre okamžite mažem.

Pridanie komentára sa nepodarilo. Oprav si prosím chyby.