La chiave per comprendere i modelli di progettazione come IIFE è rendersi conto che prima di ES6, JavaScript presentava solo l’ambito della funzione (quindi privo dell’ambito del blocco), passando i valori per riferimento all’interno delle chiusure. Questo non è più il caso, poiché la versione ES6 di JavaScript implementa l’ambito dei blocchi utilizzando le nuove parole chiave let
e const
.
Valutazione contextEdit
Una mancanza di ambito di blocco significa che le variabili definite all’interno (ad esempio) di un ciclo for avranno la loro definizione “issata” nella parte superiore della funzione che racchiude. Valutare una funzione che dipende da variabili modificate dalla funzione esterna (inclusa l’iterazione) può essere difficile. Possiamo vederlo senza un ciclo se aggiorniamo un valore tra la definizione e l’invocazione della funzione.
var v, getValue;v = 1;getValue = function () { return v; };v = 2;getValue(); // 2
Mentre il risultato può sembrare ovvio quando si aggiorna manualmente v
, può produrre risultati indesiderati quando getValue()
è definito all’interno di un ciclo.
Di seguito la funzione passav
come argomento e viene richiamata immediatamente, preservando il contesto di esecuzione della funzione interna.
var v, getValue;v = 1;getValue = (function (x) { return function () { return x; };})(v);v = 2;getValue(); // 1
Questo è equivalente al seguente codice:
var v, getValue;v = 1;function f(x) { return function () { return x; };};getValue = f(v);v = 2;getValue(); // 1
L’efficace JavaScript di David Herman contiene un esempio che illustra i problemi del contesto di valutazione all’interno dei loop. Mentre l’esempio di Herman è volutamente contorto, deriva direttamente dalla stessa mancanza di ambito di blocco.
Stabilire variabili private e accessorsEdit
IIFEs sono anche utili per stabilire metodi privati per funzioni accessibili, pur esponendo alcune proprietà per un uso successivo. Il seguente esempio viene dal post di Alman su IIFEs.
// "counter" is a function that returns an object with properties, which in this case are functions.var counter = (function () { var i = 0; return { get: function () { return i; }, set: function (val) { i = val; }, increment: function () { return ++i; } };})();// These calls access the function properties returned by "counter".counter.get(); // 0counter.set(3);counter.increment(); // 4counter.increment(); // 5
Se tentiamo di accedere acounter.i
dall’ambiente globale, non sarà definito, poiché è racchiuso all’interno della funzione invocata e non è una proprietà dicounter
. Allo stesso modo, se tentiamo di accedere a i
, si verificherà un errore, poiché non abbiamo dichiarato i
nell’ambiente globale.