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