34 Funzioni nested, IIFE, visibilità con let
Annidare (nested) delle funzioni dentro altre, permette molteplici sbocchi, amplieremo però il concetto di visibilità visto nella precedente lezione, vediamo un esempio di funzioni annidate :
var z = 1000; function esterna() { var x = 999; interna(); //interna accede anche allo scope di esterna ma non il viceversa function interna() { var y = 333; writeln(x); //OK 999 function interna_a_interna() { var a = 1; writeln(y); //OK 333 SCOPE CHAINING: il suo piu' quello di interna ... writeln(x); //OK 999 ... piu' quello di esterna } interna_a_interna(); } //writeln(y); //errore, esterna non vede lo scope di interna } esterna();
Nel contesto globale interna() non sarà accessibile, in quanto visibile solo dentro la funzione esterna(). Dentro la funzione esterna(), viene richiamata la funzione interna() che a sua volta richiama la funzione interna_a_interna().
Il meccanismo di visibilità è chiamato
chaining degli scope
detto anche concatenazione degli spazi di visibilità è quella serie di regole dove ogni funzione potrà accedere alle sue variabili ma anche a quelle contestualizzate nel suo contenitore, ovvero a quelle più esterne:
Nel nostro esempio la funzione interna() potrà accedere alla variabile x dichiarata da esterna() e anche a tutte le variabili globali.
interna() vede y, x, z
La funzione esterna() può accedere ovviamente alle sue, ma non a quelle di Interna()
esterna() vede x, z
Invece interna_a_interna() vede tutto quello a monte
interna_a_interna() vede a, y, x, z
Supponiamo di avere la variabile identificata con lo stesso nome sia nella funzione esterna che interna:
var x = "ciao"; function esterna() { function interna() { var x = 999; } }
Volendo richiamare da interna() la x esterna, dovrei farlo tramite l’oggetto window :
window.x;
La funzione contenitore non può accedere ai ‘segreti’ della funzione contenuta, il chaining degli scope non è bidirezionale.
Ogni funzione esterna può invocare la funzione interna
function esterna() { interna(); }
Ogni funzione interna eredita tutto il contenuto esterno, e volendo interna() potrebbe richiamare esterna() , ma genererebbe un loop infinito.
Richiamando in una funzione una variabile dichiarata dopo
function esterna() { alert(m); } esterna(); var m = "ciao";
otterrei un valore undefined di m. Diversamente se invoco la funzione dopo la dichiarazione della variabile, ottengo il suo valore
function esterna() { alert(m); } var m = "ciao"; esterna();
IIFE
Il pattern IIFE (Immediatly Invoke Function Expression) permette di invocare direttamente una funzione
( //operatore di raggruppamento: cio' che e' all'interno e' una espressione function esempio_iife() { var a=222; writeln("parto da sola!! " + a); } ) //fine espressione (); //operatore di chiamata di funzione
Il tutto è raggruppato dalle parentesi tonde e viene valutato come espressione, l’invocazione viene fatta con ();. Il nome scelto è a piacere, ma si potrebbe utilizzare anche la forma anonima. Espletiamo il concetto di espressione
( x = function() { alert("ciao"); } ) x();
Questa forma è equivalente a :
( x = function() { alert("ciao"); } ) ();
Quindi tutto quello che si trova dentro le parentesi tonde esterne sarebbe il contenuto eseguibile di una funzione ed è autoinvocante
();
Nell’esempio di prima il nome esempio_iife() non è invocabile esternamente o globalmente, esiste solo nel contesto delle parentesi tonde.
Ecco una forma di IIFE alternativa
( function iife() { var a=444; writeln("parto da sola!! " + a); } () );
let
Ritornando sempre sul concetto di visibilità, analizziamo il seguente esempio:
var x=5; if ( Math.random() > 0.5 ) { var x = 100; } else { var x = 200 } writeln("La x ora vale: " + x);
Ragionando come abbiamo fatto per le funzioni, viene da pensare che la x globalmente vale 5. Invece con l’if si va ad agire globalmente e la x varrà 100 se viene eseguito il primo blocco dell’if (quello tra parentesi graffe) e vale 200 se viene eseguito il blocco dell’else.
Con ECMA Script 6 viene introdotto l’uso dell’istruzione let che va a soppiantare l’uso del var. Abbiamo usato fino ad ora il var essendo enormemente ancora diffuso.
Con let andremo a proteggere il valore della variabile globale x:
x=5; if ( Math.random() > 0.5 ) { let x = 100; } else { let x = 200 } writeln("La x globale vale ancora: " + x);
Risultato: la x vale SEMPRE 5, il suo valore non viene modificato.
La x dentro all’if
{ let x = 100; }
Ha come scope e quindi come valore 100 solo all’interno del suo blocco e avremo tre variabili x distinte