Le closure in Javascript

Una delle più potenti feature di JavaScript è la possibilità di usare una
particolare tecnica di mantenimento dello stato chiamata closure. Va
detto subito che il concetto di “closure
non è stato inventato dal JavaScript; altri linguaggi ne fanno uso, talvolta
implementando anche un’apposita keyword del linguaggio stesso.

Capire bene le implicazioni di questo meccanismo non è banalissimo, ma per la sua
potenza e la sua grande esposizione ai bachi più insidiosi, permette di salire di
un gradino nella propria conoscenza di JavaScript; personalmente, ritengo che esistano
due tipologie di programmatori JavaScript: chi sa come usare le closure e chi no 🙂

Fatta la doverosa premessa, iniziamo dal principio…

Le inner function

La funzionalità di closure in JavaScript è molto comunemente esposta utilizzando non
una parola chiave particolare ma un costrutto del linguaggio chiamato inner function.

Una inner function è una funzione che viene definita all’interno di un’altra funzione.

function my_outer_function() {
  // code...
  function my_inner_function() {
    // code...
  }
  // code...
}

Una inner function, essendo creata nel local scope della funzione padre, viene distrutta
non appena quella termina. Questo vuol semplicemente dire che non è possibile, dall’esterno
della funzione padre, usare quella inner function.

function my_outer_function() {
  // code...
  function my_inner_function() {
    // code...
  }
  // code...
}

// ERRORE, my_inner_function non è visibile
my_inner_function();

Una inner function ha la curiosa capacità, però, di poter accedere direttamente
al local scope di suo padre (dato che tutto sommato ne fa parte), ovvero
a tutte le sue variabili locali.

function my_outer_function() {
  var yes_no="No";
  function my_inner_function() {
    // La inner function accede direttamente allo scope del padre,
    // per cui l'alert visualizzerà un bel "No"
    alert(yes_no);
  }
}

Grazie a questo le inner function si prestano simpaticamente ad essere utilizzate per pattern
di sviluppo molto interessanti, come il module pattern di D. Crockford, che però
esula dai contenuti questo articolo e sarà trattato successivamente.

È proprio questo comportamento che definisce il termine closure:
lo scope della inner function abbraccia/si chiude su quello del padre.

Come sfruttare una closure a nostro vantaggio

Come abbiamo detto, una inner function viene eliminata (garbage collected), non
appena termina l’esecuzione della funzione padre. Ma che succede se valorizzo una
variabile globale con una inner function anonima?

var global_something;

function my_outer_function() {
  
  global_something = function () {

  }
}

Questa sorta di scope binding comporta un effetto collaterale dal quale nasce
tutta la potenza delle closure. In questo caso, collegando il global scope
con il local scope di una funzione si fa in modo che quest’ultimo non venga
deallocato
, ma sopravvive alla “morte” della funzione che lo contiene.
Da questo deriva che anche la inner function sarà ancora utilizzabile (non è stata spazzata via).

Tecnicamente tutte le volte che JavaScript esegue una funzione vengono allocati
internamente due oggetti: uno è un riferimento alla funzione stessa, e l’altro
è un oggetto di tipo scope… solitamente quest’ultimo viene rilasciato alla fine
della funzione, a meno che non sia stato collegato/referenziato da un altro oggetto.

Dunque non solo sarà possibile usare la inner function dall’esterno del suo
padre, ma sarà ancora possibile accedere alle variabili del local scope
dall’interno di essa!

var global_something;

function my_outer_function() {
  var yes_no="yes";
  global_something = function () {
    alert("Am I alive? " + yes_no);
  }
}

my_outer_function();

// Visualizzerà un confortante "Am I alive? yes"
global_something();

Il fatto che lo scope dove la inner function è stata dichiarata non venga
deallocato, implica un altro interessante aspetto da tenere assai presente (leggi:
fonte di bachi e “strani” comportamenti): quando la inner function verrà usata, il
valore delle variabili del suo scope sarà quello raggiunto al termine
della funzione padre.

var global_something;
function my_outer_function() {
  global_something = function () {
    alert("Loops: " + i);
  }
  
  for (var i=0; i < 1000; i++) {
    // ...code...
  }
}
my_outer_function();
// Visualizzerà "Loops: 1000", che è l'ultimo valore raggiunto da "i"
global_something();

Avere accesso ad uno scope, ovviamente può anche voler dire esser in grado di
modificarlo. Vediamo...


function my_outer_function() {
  var counter=0;
  
  increase_counter=function() {
    counter++;
  }
  
  show_counter=function() {
    alert(counter);
  }
}

my_outer_function();

// Visualizzerà "0"
show_counter();

// Lo scope non è "frozen"... lo posso ancora modificare
increase_counter();

// Visualizzerà "1"
show_counter();

Una nota a latere per i meno esperti: se una variabile viene valorizzata all'interno di una
funzione, senza prima essere stata dichiarata con "var", allora questa verrà creata nel
global scope (ovvero come proprietà dell'oggetto window). Nell'esempio
precedente è il caso di increase_counter e show_counter.

Vediamo adesso un esempio che dimostra il concetto espresso più sopra: ogni volta
che una funzione viene chiamata, l'interprete JavaScript alloca un nuovo oggetto scope per essa...


function my_outer_function() {
  var counter=0;
  
  increase_counter=function() {
    counter++;
  }
  
  show_counter=function() {
    alert(counter);
  }
}

my_outer_function();

// Visualizzerà "0"
show_counter();

increase_counter();

// Una nuova chiamata alla funzione che crea la closure
// Questa sarà chiusa su un NUOVO oggetto scope. Le inner function stesse 
// saranno nuovamente ricreate... il vecchio oggetto scope è "perso".
my_outer_function();

// Visualizzerà "0" (non è stata chiamata la increase_counter)
show_counter();

Closure, oggetti e il fantomatico this

Fin qui abbiamo fatto esempi piuttosto semplici. Proviamo a giocare un po' con
gli oggetti e scopriremo alcune cose interessanti:

Come sappiamo, tutte le volte che una funzione viene chiamata, a questa viene
sempre automaticamente passato (nel local scope) un particolare oggetto chiamato this. Esso
deve essere pensato come il "contesto" all'interno del quale la funzione si troverà
ad essere eseguita.


function beat_me() {
  alert(this);
}

// Il contesto di default di una funzione è il global scope, ovvero in un browser 
// sarà l'oggetto "window"... questo è quello che il nostro alert visualizzerà
beat_me();

Aggiungiamo una inner function, e vediamo che succede...


function beat_me() {
  beat_me_inner = function() {
    alert(this);
  }
}

beat_me();

// Ancora "window"
beat_me_inner();

Niente di strano (pare)... la inner function continua a visualizzare window
come oggetto this... si potrebbe pensare che si tratti, per quanto detto finora,
dello stesso oggetto che si trova nel local scope del padre.

Purtroppo, ahimé, non è così. Vediamo perché.

Una funzione può essere utilizzata per "costruire" nuovi oggetti, un po' come
nella OO "class-ica" esistono i metodi constructor. Usiamo l'inflazionatissimo
oggetto Person per fare un esempio.

// La prima lettera maiuscola del nome è solo convenzionale
function Person() {
  alert(this)
  
  this.name=null; // Giusto per far qualcosa...
  
  // Il return è inutile, dato che è un comportamento implicito... giusto per chiarezza
  return this;
}

var claudio = new Person();

L'operatore new non fa altro che modificare il comportamento tale per cui
ad una funzione viene passato window come this. In questo caso, quello
che viene passato è un oggetto tutto nuovo, pronto per essere augmented.
Il nostro alert visualizzerà un generico oggetto Object.

Un riferimento al nuovo oggetto augmented sarà poi assegnato alla
variabile "claudio" (un po' come fosse un'istanza...) appena la funzione Person
terminerà (per questo nell'esempio dico che il return è inutile).

Aggiungiamo a questo punto una inner function all'interno del nostro oggetto, per
dare ancora un'occhiata...

function Person() {
  this.name=null; // Giusto per...
  
  inspect_this = function() {
    alert(this);
  }
}

var claudio = new Person();

inspect_this();

Il comportamento non sarà quello atteso: il this di quella inner function all'interno
dell'oggetto è... window, e non l'oggetto nel quale quella inner function viene creata.

Occorre dunque usare una closure sullo scope dell'oggetto, usando un escamotage
di questo tipo:

function Person(name, surname) {
  this.name=name;
  this.surname=surname;
  
  // Usiamo una variabile locale, che fantasiosamente chiameremo "that"
  var that=this;
  
  inspect_this = function() {
    alert(that.name + " " + that.surname);
  }
}

var claudio = new Person("Claudio", "Cicali");

// "Claudio Cicali"
inspect_this();

Qualcuno potrebbe obiettare che questo esempio non ha molto senso. In realtà vi
posso assicurare che in un programma di una certa complessità, prima o poi ognuno
si ritroverà a scontrarsi con questo piccolo inghippo. Uomo avvisato, mezzo salvato!

Closure e DOM

Creiamo adesso un oggetto con la notazione object literal, tanto comoda
per creare singleton e/o namespace, e utilizziamolo per degli esempi da fare
sugli utilizzi di closure e inner function nella gestione degli event handler
del DOM HTML.

Nel primo esempio non farò altro che creare un'applicazione ed eseguire il suo
metodo di init; questo cercherà un ipotetico elemento con id "about" e associerà
una funzione anonima al suo evento "click" per visualizzare il nome dell'applicazione.
(Mi si perdoni se, per ovvi motivi, non uso addEventListener o similari).


var application = {
  name: "Foobar 1.0",
  init: function() {
    document.getElementById('about').onclick = function() {
      alert("Applicazione " + this.name);
    }
  }
}

function bootstrap() {
  application.init();
}

window.onload=bootstrap;

Se avete seguito quanto detto finora, non vi stupirà scoprire che questo esempio
non funziona. Ancora una volta il problema è in quel this.name. Quando
l'evento è sparato, verrà sì eseguita la funzione anonima definita
nel nostro singleton, ma in che contesto? Per comodità nostra, il browser ci passerà
come this l'elemento su cui abbiamo cliccato... di application, ahinoi,
nessuna traccia...

Ecco dunque che potremmo ripescare il that di prima:


var application = {
  name: "Foobar 1.0",
  init: function() {
    var that=this;
    document.getElementById('about').onclick = function() {
      alert("Applicazione " + that.name);
    }
  }
}

function bootstrap() {
  application.init();
}

window.onload=bootstrap;

Closure, memoria e IE6

Sembrava impossibile aver scritto un articolo su tecnologie web senza parlar male
di Internet Explorer, non è vero?

Il sistema delle closure ha un lato oscuro, e questo lato oscuro riguarda
la gestione della memoria dei browser. Senza entrare in troppi faticosi dettagli,
quello che è importante sapere è che esiste un meccanismo, detto garbage collector
che fa sì che la memoria non più utilizzata venga riciclata ad uso di altre parti
del programma.

Ora: all'interno dell'ambiente nel quale viene eseguito il codice JavaScript,
ovvero il nostro browser, si distinguono almeno due grandi "zone", ognuna delle
quali ha il proprio garbage collector. Una di queste zone è l'ambiente JavaScript
vero e proprio dove si trovano gli oggetti, le funzioni, gli oggetti scope, gli oggetti
predefiniti del linguaggio ecc, mentre l'altra zona è quella dove risiede il
documento che ad un certo momento è caricato (che chiameremo semplicisticamente "DOM")

Finché questi due "mondi" rimangono tra loro distinti, il loro garbage collector
fa il suo dovere senza problemi. Ma appena i due mondi si intersecano, possono
succedere cose brutte (si legga memory leak). In questo caso infatti
si ha una situazione di stallo, perché ognuno dei due GC è in un certo qual modo
bloccato dall'altro (problema della circular reference)..

In realtà, le cose non sono così gravi; nei browser più recenti (Internet Explorer 7
compreso) i GC sono abbastanza intelligenti da cavarsi tranquillamente d'impiccio.
Ma non IE6.

Facciamo un esempio:

function IE6_leaks() {
  var x=new Array(1000).join(new Array(3000).join("XXXXX"));
  var el=document.getElementById('first');
  el.onclick=function() {

  }
}

window.onload=IE6_leaks;

Ogni volta che questa innocua funzioncina viene eseguita (ad ogni nuovo caricamento della
pagina) IE6 occuperà 30KB, senza rilasciarli. Se insisterete con il vostro Ctrl+R,
alla fine vi troverete con la memoria completamente occupata.

Il problema sta in quella funzione anonima. La funzione anonima è un oggetto JavaScript,
ma l'evento (e l'elemento "el" sul quale viene definito l'handler) è un oggetto
del DOM. L'utilizzo della funzione anonima, poi, crea una closure sullo scope
della funzione padre, che contiene un oggetto "x" enorme... e il danno è fatto.

Come accennavo, questo comportamento sembra sparito in IE7 (perlomeno non si presenta
in maniera così clamorosa).

Nei riferimenti trovate un paio di articoli un po' più approfonditi su questo problema
con dei metodi per risolverlo.

Closure: tre esempi non banali (e forse utili)

1. Come posso passare un parametro ad un event handler?


  var el=document.getElementById('about');
  el.addEventListener('click', (function(str) {
    return function() {
      alert(str);
    }
  })("Hello"), false);


2. Ho un certo numero di elementi P ed un array contenente il testo da inserire
in ognuno di essi, appena ci clicco sopra.

  var contents=["uno","due","tre","quattro"];
  var els=document.getElementsByTagName('P');
  for (var i=0; i < els.length; i++) {
    els[i].onclick=(function(index) {
      return function() {
        this.innerHTML=contents[index];
      }
    })(i);
  }

In base a quanto detto nell'articolo, provate a capire perché questa invece
non funzionerà:

  var contents=["uno","due","tre","quattro"];
  // Presupponiamo l'esistenza di esattamente 4 elementi P 😉
  var els=document.getElementsByTagName('P');
  for (var i=0; i < els.length; i++) {
    els[i].onclick=function() {
      this.innerHTML=contents[i]; 
    }
  }

3. Come posso passare un parametro ad una funzione eseguita con setTimeout?

function greets_person(person) {
  return (function() {
    alert("Hello, " + person)
  });
}

var delayed=greets_person('Claudio Cicali');

setTimeout(delayed, 1000);

Vi lascio con un quesito. Mentre preparavo gli esempi per questo articolo, mi
sono imbattuto in un esempio che DOVREBBE creare dei memory leak in IE6, ma invece non lo fa...

Sapete dire perché? (io no)


function this_does_not_leak() {
  var x=new Array(1000).join(new Array(3000).join("XXXXX"));
  var elm=document.getElementsByTagName('P');
  for (var i=0; i < elm.length ; i++) {
    elm[i].onclick=function() {}
  }
}

function this_leaks() {
  var x=new Array(1000).join(new Array(3000).join("XXXXX"));
  var elm=document.getElementsByTagName('P');
  for (var i=0; i < elm.length ; i++) {
    var u=elm[i];
    u.onclick=function() {}
  }
}


Riferimenti:

Comments

  1. > personalmente, ritengo che esistano due tipologie di programmatori JavaScript: chi sa come usare le closure e chi no 🙂

    Prima di leggerti appartenevo alla seconda categoria. Illuminante, grazie.

  2. Anche io! Claudio, grazie per avermi introdotto tra gli adepti! 🙂

  3. cavolo, la differenza tra le funzioni versione leaking/non-leaking è incredibile.

    Una spiegazione assolutamente inventata potrebbe essere: il GC lato js non è in grado di gestire il refcount per gli Element, ma lo fa correttamente per gli oggetti NodeList.
    Avendo un solo riferimento a una NodeList la marca come refcount=0, per cui viene sottoposta a collection, e di conseguenza il GC lato DOM fa collection degli oggetti Element.
    Quando entrambi i GC hanno riferimenti a un preciso element vanno in conflitto.

    Ovviamente non ho alcun elemento a supporto dell’ipotesi, e neanche IE per provare 🙂

  4. mh.. continuando a pensarci, credo che quello che dico abbia senso 🙂

    Un element per sua natura è strettamente connesso con tutti gli altri dom, avendo riferimenti sia in ingresso che in uscita. Un oggetto NodeList invece ha riferimenti agli element, ma non è rerenziato dagli stessi. Credo.

  5. > Le inner function

    Personalmente evito come la peste l’uso di variabili globali. Di solito, passo alla function un oggetto e attacco ad esso le funzioni, oppure restituisco la funzione generata tramite return.

    Ad esempio, questa è una funzione di “precompilazione” dei parametri:

    private function makeFactory(fx, …argsPre) {
    var self = this;
    return function(…argsPost) {
    fx.apply(self, argsPre.concat(argsPost));
    }
    }

    > Closure, oggetti e il fantomatico this

    Personalmente preferisco usare “var self = this”, è più chiaro. 🙂

    Il comportamento di this però non è affatto un “piccolo inghippo”: il motivo di quel comportamento è chiaro e ben strutturato.

    La definizione è questa: “this si riferisce allo scope in cui function viene chiamata”.

    E’ questo il motivo per cui succede quello, e per cui il comportamento sembra strano. In realtà non lo è. Anzi, è una feature del linguaggio molto importante, perché permette di scrivere una function x() in una parte del codice e bindarla in un’altra parte del codice, utilizzando “this” in modo che funzioni una volta associata al nuovo oggetto. 🙂

    Nell’ereditarietà prototipale (ovvero, ECMAScript/JavaScript/ActionScript) questa cosa è molto importante ed utilizzata. 🙂

    ~

    Aggiungerei un altro costrutto che ritengo interessante:

    var Classe = function() { this.initialize.apply(this, arguments); }

    In questo modo, poi posso dichiarare la classe come hai fatto sopra:

    Class.prototype = {
    initialize: function() {…}
    method: …,
    property: …
    }

    E istanziarla:

    var c = new Class();

    Nota anche l’uso di prototype, che associa la struttura dati con la meta-classe ottimizzando il consumo di memoria. Se venisse fatto senza, in memoria si replicherebbe tutta la struttura dati ogni volta che viene creato un nuovo oggetto. 😉

    Pensare che giusto ieri sera volevo iniziare a scrivere una serie di post sull’utilizzo avanzato di ActionScript et simili :asd:

  6. Errore mio: la sintassi:
    function name(a, b, …args) è di AS3, mentre in JavaScript conviene ancora usare la variabile di classe interna a function, arguments. 🙂

  7. @folletto: “piccolo inghippo” non voleva sottolineare neinte di più che un comportamento un po’ controintuitivo sebbene corretto (e non sono tutti d’accordo sulla sua correttezza, Crockford compreso).

    Per quanto riguarda i pattern “Class”, credo sia errato il volerlo proporre: JavaScript non le ha, è un liguaggio di “altro tipo”, e cercare di simularle, sebbene comodo, rischia di creare più confusione di quella che già con c’è. E’ per questo che non ho molto in simpatia Prototype, per esempio (che ha pure un “coso” Hash… vabbé).

    JS non è inferiore perché non ha le classi.. usa un altro paradigma: perché non limitarsi ad usare quello?

    Il discorso sul prototype “esterno” alla definizione sarà, spero, argomento di un altro articolo (sugli oggetti)

    Grazie del commento

  8. Dimenticavo… “self” non si può usare perché è un sinonimo di “window”.

  9. Si, so dei dubbi sulla correttezza, ma senza sarebbe veramente un problema sfruttare certe situazioni. 🙂
    Su self come window, non credo ci sian problemi, nel senso che lo scope dovrebbe essere differente ed essendo sinonimo non inficia le funzionalità, o sbaglio? 🙂
    E’ che “that” è morfologicamente troppo simile a “this” e causa una eccessiva confusione visuale a mio avviso. 🙂

    Per quanto riguarda Class sono perfettamente d’accordo (odio Prototype, long live jQuery), ma non credo sia un problema in questo specifico caso.
    Siamo perfettamente d’accordo che l’ereditarietà prototipale E’ un’altra cosa e che JavaScript NON è meno potente per questo, anzi: con i prototipi posso emulare ereditarietà ad oggetti ma non viceversa. Questo la dice lunga su chi abbia la struttura più espressiva. 🙂
    Io amo ECMAscript (a parte alcune sue idiosincrasie) e più in generale le strutture avanzate dei linguaggii.

    Ritengo che vada usato quello che è più intuitivo ed efficiente.
    In questo caso, penso che non ci sia niente di male usare un più intuitivo costrutto new, che è soltanto sintatticamente differente da .init().
    A livello di codice, scrivere:
    var a = {}
    a.init()
    oppure:
    var A = {}
    var a = new A();
    E’ in termini pratici soltanto una differenza sintattica, ma la seconda formula ne guadagna per intuitività. 🙂

    Non solo, ma questo è anche quanto Crockford cita: “new” è il costrutto usato da JavaScript per l’ereditarietà prototipale e non significa per forza stare emulando l’ereditarietà classica. Il trick segnalato rende sì l’oggetto analogo ad una Classe “classica”, ma è anche una shortcut per fare due chiamate in una nell’ereditarietà prototipale. 🙂

    Insomma: non è l’uso di “new” che rappresenta l’ereditarietà classica, né significa che la stia perseguendo. ECMAscript usa new per questo scopo… ce lo dobbiamo tenere. 😛

    Poi beh, se uno vuole può wrapparlo come fa Crockford nell’articolo che cito, ma ritengo che la soluzione che ho esposto prima sia più pulita, anche se strutturalmente meno efficiente. 🙂

  10. L’avevo sempre usato, ma per sicurezza ho controllato prima di rispondere: come citavo sopra, non c’è alcun problema ad usare “self” perché lo scope è differente (var self è sempre risolto prima) e “window” è comunque preferibile per questione di chiarezza.

    Potrei dire con una certa sicurezza che non vi dovrebbero essere problemi a partire da IE5.5+ e analoghi (perché è il limite sul quale di solito sviluppo), ma non riesco a trovare documentazione su quando apparve window come oggetto. Nel caso fosse presente sin dalle primissime versioni, allora non v’è quasi matematicamente alcun problema. 🙂

  11. Ne prendo atto 😉 Il mio affrettato “non si può usare” va ripensato con quanto tu dici.

    Ciao

  12. Io personalmente adoro ECMAscript proprio perche’ usa il paradigma a prototipi 😀 Infatti ho sempre sbavato (e sbavo ultimamente che sto facendo un giochino in JS + svg) sulle chiusure e sul cambiamento di contesto di this.

    Interessanti i commenti di Folletto e in proposito (ma un po’ OT) al cambiamento del contesto, ho scoperto che e’ molto molto utile usando le callbacks.
    In particolare, in una situazione simile (non di eredita’, ma di delegazione):
    var o1 = {
    init: function(caller, cb) { this.caller = caller; this.callback = cb;},
    call: function() { this.callback.call(caller); }
    }
    var o2 = {
    deleg: o1,
    ask: function() { this.o1.init(this, this.process); this.o1.call(); }
    process: function() { alert(“Foo”); }
    }
    Forse si puo’ migliorare, ma l’ho trovato utile per separare una classe che utilizza ajax per sincronizzare client e server, ed un’altra classe che si occupa dell’interfaccia utente. In questo modo i codici rimangono ben separati e il meccanismo delle callback si puo’ usare con tranquillita’ – con delle funzioni membro nello scope che ci si aspetta.

    Bell’articolo!

  13. Flyingfrog says:

    bello bello bello che esistiate … avessi letto questo articolo 6 mesi fa mi sarei risparmiato qualche notte insonne …. complimenti comunque a tutta la redazione. Per chi fosse interessato ad approfondire le proprie conoscenze su javascript, ecco dove ho passato le mie notti insonni :
    http://www.crockford.com/javascript/

  14. var application = {
    name: “Foobar 1.0”,
    init: function() {
    var that=this;
    document.getElementById(‘about’).onclick = function() {
    alert(“Applicazione ” + that.name);
    }
    }
    }

    function bootstrap() {
    application.init();
    }

    window.onload=bootstrap;

    puoi anche fare una cosa del genere (e in genere si fa 😉 )

    var application = {
    name: “Foobar 1.0”,
    init: function() {
    document.getElementById(‘about’).onclick = function() {
    alert(“Applicazione ” + application.name);
    }
    }
    }

    function bootstrap() {
    application.init();
    }

    window.onload=bootstrap;

    Ciao.

  15. stefano says:

    Articolo fantastico, mi hai risolto un problema!

    dovevo fare uno script ajax, ma gli esempi che ci sono in giro usano un unico oggetto globale in cui memorizzano l’istanza della request… ma se faccio due chiamate ajax di fila, la seconda chiamata va a sovrascrivere la prima!
    e non sapevo come uscirne… grazie al tuo articolo invece ho risolto!

  16. Articolo veramente molto interessante!

    Volevo chiedere solo una cosa relativamente all’esempio 1 del passaggio di un parametro ad un evento (con una piccola variazione…)

    var el=document.getElementById(‘about’);
    el.addEventListener(‘click’, (function(num) {
    return function() {
    num++;
    alert(num);
    }
    })(50), false);

    Ogni volta che clicco sull’elemento ‘about’ la variabile num viene incrementata (partendo da 50) e poi visualizzata.

    Qual’è lo scope di ‘num’? Posso agire su ‘num’ solo cliccando sull’oggetto ‘about’… posso accedere a questa variabile anche dall’esterno della funzione stessa tramite ad esempio un’altra funzione?

    Mi scuso per la domanda, forse banale, ma in questo caso proprio non sono riuscito a capire dov’è il “trucco”…

    Grazie
    Diego

Policy per i commenti: Apprezzo moltissimo i vostri commenti, critiche incluse. Per evitare spam e troll, e far rimanere il discorso civile, i commenti sono moderati e prontamente approvati poco dopo il loro invio.