Le chiusure in JavaScript sono uno di quei concetti che molti fanno fatica a capire. Nel seguente articolo, spiegherò in termini chiari cos’è una chiusura, e porterò il punto a casa usando semplici esempi di codice.
Una chiusura è una caratteristica in JavaScript in cui una funzione interna ha accesso alle variabili della funzione esterna (che racchiude) – una catena di scope.
La chiusura ha tre catene di scope:
- ha accesso al proprio scope – le variabili definite tra le sue parentesi graffe
- ha accesso alle variabili della funzione esterna
- ha accesso alle variabili globali
Per i non iniziati, questa definizione potrebbe sembrare solo un sacco di gergo!
Ma cos’è veramente una chiusura?
Guardiamo un semplice esempio di chiusura in JavaScript:
function outer() { var b = 10;
function inner() {
var a = 20;
console.log(a+b);
}
return inner;
}
Qui abbiamo due funzioni:
- una funzione esterna
outerche ha una variabileb, e restituisce lainnerfunzione - una funzione interna
innerche ha la sua variabile chiamataa, e accede ad una variabileoutervariabileb, all’interno del suo corpo di funzione
Lo scopo della variabile b è limitato alla funzione outer, e lo scopo della variabile a è limitato alla funzione inner.
Invochiamo ora la funzione outer() e memorizziamo il risultato della funzione outer() in una variabile X. Invochiamo quindi la funzione outer() una seconda volta e memorizziamola nella variabile Y.
Vediamo passo dopo passo cosa succede quando la funzione outer() viene invocata per la prima volta:
- La variabile
bviene creata, il suo ambito è limitato alla funzioneouter()e il suo valore viene impostato a10. - La prossima linea è una dichiarazione di funzione, quindi niente da eseguire.
- Nell’ultima riga,
return innercerca una variabile chiamatainner, trova che questa variabileinnerè in realtà una funzione, e quindi restituisce l’intero corpo della funzioneinner. - Il contenuto restituito dalla dichiarazione di ritorno è memorizzato in
X.
Quindi,Xmemorizzerà quanto segue:function inner() {
var a=20;
console.log(a+b);
} - La funzione
outer()termina l’esecuzione, e tutte le variabili nello scope diouter()ora non esistono più.
Questa ultima parte è importante da capire. Una volta che una funzione completa la sua esecuzione, tutte le variabili che sono state definite all’interno dello scope della funzione cessano di esistere.
La durata di una variabile definita all’interno di una funzione è la durata dell’esecuzione della funzione.
Questo significa che in console.log(a+b), la variabile b esiste solo durante l’esecuzione della funzione outer(). Una volta che la funzione outer ha terminato l’esecuzione, la variabile b non esiste più.
Quando la funzione viene eseguita la seconda volta, le variabili della funzione vengono create di nuovo, e vivono solo fino a quando la funzione termina l’esecuzione.
Quindi, quando outer() viene invocato la seconda volta:
- viene creata una nuova variabile
b, il suo ambito è limitato alla funzioneouter(), e il suo valore viene impostato a10. - La linea successiva è una dichiarazione di funzione, quindi niente da eseguire.
-
return innerrestituisce l’intero corpo della funzioneinner. - Il contenuto restituito dalla dichiarazione di ritorno è memorizzato in
Y. - La funzione
outer()termina l’esecuzione, e tutte le variabili nello scope diouter()ora non esistono più.
Il punto importante qui è che quando la funzione outer() viene invocata per la seconda volta, la variabile b viene creata nuovamente. Inoltre, quando la funzione outer() finisce di essere eseguita la seconda volta, questa nuova variabile b cessa di esistere.
Questo è il punto più importante da realizzare. Le variabili all’interno delle funzioni esistono solo quando la funzione è in esecuzione, e cessano di esistere una volta che la funzione completa l’esecuzione.
Ora, torniamo al nostro esempio di codice e guardiamo X e Y. Poiché la funzione outer() in esecuzione restituisce una funzione, le variabili X e Y sono funzioni.
Questo può essere facilmente verificato aggiungendo quanto segue al codice JavaScript:
console.log(typeof(X)); //X is of type function
console.log(typeof(Y)); //Y is of type function
Siccome le variabili X e Y sono funzioni, possiamo eseguirle. In JavaScript, una funzione può essere eseguita aggiungendo () dopo il nome della funzione, come X() e Y().
Quando eseguiamo X() e Y(), stiamo essenzialmente eseguendo la funzione inner .
Esaminiamo passo dopo passo cosa succede quando X() viene eseguito per la prima volta:
- Variabile
aviene creata, e il suo valore viene impostato a20. - JavaScript ora cerca di eseguire
a + b. Qui è dove le cose si fanno interessanti. JavaScript sa cheaesiste poiché lo ha appena creato. Tuttavia, la variabilebnon esiste più. Poichébfa parte della funzione esterna,besisterebbe solo mentre la funzioneouter()è in esecuzione. Poiché la funzioneouter()ha terminato l’esecuzione molto prima che noi invocassimoX(), tutte le variabili nell’ambito della funzioneoutercessano di esistere, e quindi la variabilebnon esiste più.
Come gestisce JavaScript questo?
Chiusure
La funzione inner può accedere alle variabili della funzione racchiusa grazie alle chiusure in JavaScript. In altre parole, la funzione inner conserva la catena di scope della funzione di chiusura nel momento in cui la funzione di chiusura è stata eseguita, e quindi può accedere alle variabili della funzione di chiusura.
Nel nostro esempio, la funzione inner aveva conservato il valore di b=10 quando la funzione outer() è stata eseguita, e continua a conservarlo (chiusura).
Ora si riferisce alla sua catena di scope e nota che ha il valore della variabile b all’interno della sua catena di scope, poiché aveva racchiuso il valore di b all’interno di una chiusura nel punto in cui la funzione outer era stata eseguita.
Quindi, JavaScript conosce a=20 e b=10, e può calcolare a+b.
Puoi verificarlo aggiungendo la seguente linea di codice all’esempio sopra:
Apri l’elemento Inspect in Google Chrome e vai alla Console. Puoi espandere l’elemento per vedere effettivamente l’elemento Closure (mostrato nella terzultima riga sotto). Notate che il valore di b=10 è conservato nel Closure anche dopo che la funzione outer() completa la sua esecuzione.

Ripercorriamo ora la definizione di chiusura che abbiamo visto all’inizio e vediamo se ora ha più senso.
Quindi la funzione interna ha tre catene di scope:
- accesso al proprio scope – variabile
a - accesso alle variabili della funzione
outer– variabileb, che ha racchiuso - accesso a qualsiasi variabile globale che può essere definita
Chiusure in azione
Per portare a casa il punto delle chiusure, aumentiamo l’esempio aggiungendo tre righe di codice:
Quando si esegue questo codice, si vedrà il seguente output nel console.log:
a=20 b=10
a=20 b=11
a=20 b=12
a=20 b=10
Esaminiamo questo codice passo dopo passo per vedere cosa succede esattamente e per vedere le chiusure in azione!
var X = outer(); // outer() invoked the first time
La funzione outer() viene invocata la prima volta. I seguenti passi hanno luogo:
- Variabile
bviene creata, ed è impostata su10Variabilecviene creata, e impostata su100
Chiamiamo questob(first_time)ec(first_time)per nostro riferimento. - La funzione
innerviene restituita e assegnata aXA questo punto, la variabilebè racchiusa nella catena di scope della funzioneinnercome chiusura conb=10, poichéinnerutilizza la variabileb. - La funzione
outercompleta l’esecuzione e tutte le sue variabili cessano di esistere. La variabilecnon esiste più, anche se la variabilebesiste come chiusura dentroinner.
var Y= outer(); // outer() invoked the second time
- La variabile
bviene creata nuovamente ed è impostata su10La variabilecviene creata nuovamente.
Nota che anche seouter()è stato eseguito una volta prima che le variabilibeccessassero di esistere, una volta che la funzione ha completato l’esecuzione vengono create come variabili nuove.
Chiamiamo questeb(second_time)ec(second_time)per nostro riferimento. - La funzione
innerviene restituita e assegnata aYA questo punto la variabilebè racchiusa nella catena di scope della funzioneinnercome chiusura conb(second_time)=10, poichéinnerusa la variabileb. - La funzione
outercompleta l’esecuzione, e tutte le sue variabili cessano di esistere.
La variabilec(second_time)non esiste più, anche se la variabileb(second_time)esiste come chiusura dentroinner.
Ora vediamo cosa succede quando vengono eseguite le seguenti linee di codice:
X(); // X() invoked the first time
X(); // X() invoked the second time
X(); // X() invoked the third timeY(); // Y() invoked the first time
Quando X() viene invocato per la prima volta,
- viene creata la variabile
a, e impostata su20 - il valore di
a=20, il valore dibè quello della chiusura.b(first_time), quindib=10 - le variabili
aebsono incrementate da1 -
X()completa l’esecuzione e tutte le sue variabili interne – variabilea– cessano di esistere.
Tuttavia,b(first_time)è stato conservato come chiusura, quindib(first_time)continua ad esistere.
Quando X() viene invocato la seconda volta,
- la variabile
aviene creata nuovamente, e impostata su20Qualsiasi valore precedente della variabileanon esiste più, poiché ha cessato di esistere quandoX()ha completato l’esecuzione la prima volta. - il valore di
a=20il valore dibè preso dal valore di chiusura –b(first_time)Nota anche che abbiamo incrementato il valore dibdi1dalla precedente esecuzione, quindib=11 - le variabili
aebsono aumentate di1di nuovo -
X()completa l’esecuzione e tutte le sue variabili interne – variabile a – cessano di esistere
Tuttavia,b(first_time)è conservato in quanto la chiusura continua ad esistere.
Quando X() viene invocato la terza volta,
- la variabile
aviene creata nuovamente, e impostata su20Qualsiasi valore precedente della variabileanon esiste più, poiché ha cessato di esistere quandoX()ha completato l’esecuzione la prima volta. - il valore di
a=20, il valore dibè dal valore di chiusura –b(first_time)
Nota anche che avevamo incrementato il valore dibdi1nella precedente esecuzione, quindib=12 - le variabili
aebsono incrementate da1di nuovo -
X()completa l’esecuzione, e tutte le sue variabili interne – variabilea– cessano di esistere
Tuttavia,b(first_time)viene conservato in quanto la chiusura continua ad esistere
Quando Y() viene invocato per la prima volta,
- la variabile
aviene creata nuovamente e impostata su20 - il valore di
a=20, il valore dibè dal valore di chiusura –b(second_time), quindib=10 - le variabili
aebsono incrementate di1 -
Y()completa l’esecuzione, e tutte le sue variabili interne – variabilea– cessano di esistere
Tuttavia,b(second_time)è stato conservato come chiusura, quindib(second_time)continua ad esistere.
Riservazioni conclusive
Le chiusure sono uno di quei concetti sottili in JavaScript che sono difficili da afferrare all’inizio. Ma una volta che le hai capite, ti rendi conto che le cose non avrebbero potuto andare diversamente.
Spero che queste spiegazioni passo dopo passo ti abbiano aiutato a capire davvero il concetto di chiusura in JavaScript!
Altri articoli:
- Una guida rapida alle funzioni “autoinvocanti” in Javascript
- Comprendere lo scope di funzione vs. lo scope di blocco in Javascript
- Come usare le Promesse in JavaScript
- Come costruire una semplice animazione Sprite in JavaScript