martedì 16 ottobre 2012

Closures


In questo nuovo appuntamento parlerò di closures. Come per quanto riguarda l'hoisting, le closures sono un concetto fondamentale per comprendere appieno la potenza di JavaScript, in quanto permettono di creare funzioni personalizzate che vincolano le variabili in un ambito. Sono usate generalmente per costruire funzioni di callback.

Una buona definizione di closure si può trovare sul sito Mozilla Developers Network, da cui questo articolo prende ispirazione:

"una closure è un oggetto particolare che combina due cose: una funzione e l'ambiente in cui la funzione è stata creata."

Per capire meglio partiamo con un primo esempio:

function init() {
  var myName = "Mauri";
  function displayName() {
    alert(name);
  }
  displayName();
}
init();
la funzione init() crea una variabile locale myName (che possiamo considerare come parte dell'environment o ambiente) e definisce una funzione interna chiamata displayName(). Questa funzione dispone della variabile dichiarata e inizializzata nella funzione esterna. Il funzionamento di init() è intuitivo ed è un semplice esempio di functional scoping, nel quale le funzioni annidate possono accedere alle variabili dichiarate all'esterno.

Ora proviamo a modificare l'esempio come segue:
function makeDisplay() {
  var myName = "Mauri";
  function displayName() {
    alert(name);
  }
  // NB: una funzione è una variabile,quindi può essere ritornata
  return displayName;
}

var display = makeDisplay();
display();
Il comportamento di questo codice è identico a quello precedente, potete provarlo comodamente su jsFiddle. Ciò che distingue questo esempio è il fatto che la funzione interna viene ritornata dalla funzione esterna prima di essere eseguita. Intuitivamente ci si aspetta che, terminata l'esecuzione di makeDisplay(), la variabile interna myName sia cancellata dallo stack (come avviene nei linguaggi della famiglia C) e non possa venire referenziata da display(), tuttavia l'esecuzione del codice dimostra che la variabile è ancora accessibile è può essere utilizzata.

Questo peculiare aspetto è propro ciò che caratterizza le closures. La variabile myFunction "racchiude" al suo interno la funzione displayName() e le eventuali variabili accessibili alla funzione durante la creazione della closure. Un esempio (variazione spudorata di quello su MDN) un pochino più complesso può essere il seguente:
function increment(base) {
    this.base = base;
    return function(value) {
        return base + value;
    }
} 

var addTo5 = increment(5);
var addTo14 = increment(14);

alert(addTo5(5)); // output: 10
alert(addTo14(3)); // output: 17
La funzione increment in questo caso è un esempio di function factory, ovvero una funzione che aggiunge un valore specifico all'argomento passato. Questa function factory è stata utilizzata per creare due nuove funzioni (addTo5 e addTo14) che impostano rispettivamente, nel proprio ambiente, i valori 5 e 14 come base a cui verranno aggiunti gli argomenti passati (5 e 2). addTo5 e addTo14 sono quindi closures.

E' importante ricordarsi che le variabili interne sono passate per riferimento e non per copia:
function dica33() {
  var num = 32;
  var showAlert = function() {
    alert(num);
  }
  num++;
  return showAlert();
}

var show33 = dica33();
show33();
Per oggi mi fermo qui, ma non è detto che in futuro possa portare ulteriori approfondimenti sul tema.

Nessun commento:

Posta un commento