Closure in TypeScript
In questo articolo, spiegheremo le closures in TypeScript.
YouTube Video
Closure in TypeScript
Cos'è una Closure?
Una closure si riferisce alla capacità di mantenere i riferimenti allo scope (ambiente) in cui una funzione è stata definita, anche se la funzione viene chiamata al di fuori di quello scope. Di seguito, le closures verranno spiegate includendo le annotazioni di tipo.
In parole semplici, una closure combina una funzione e l'ambiente delle variabili in cui la funzione è stata definita, permettendo di accedere a quell'ambiente quando la funzione viene chiamata.
Meccanismo di base delle Closures
In TypeScript, quando una funzione viene definita all'interno di un'altra funzione, diventa evidente che la funzione interna può accedere alle variabili della funzione esterna. Ecco un esempio base di closure con annotazioni di tipo.
1function outerFunction(): () => void {
2 let outerVariable: string = "I am from outer function";
3
4 function innerFunction(): void {
5 // The inner function accesses the variable of the outer function
6 console.log(outerVariable);
7 }
8
9 return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure(); // "I am from outer function"
- Il tipo di ritorno di
outerFunction
è() => void
, il che indica che restituisce una funzione. - Il tipo di
innerFunction
è esplicitamente impostato suvoid
, il che indica che non restituisce alcun valore.
Utilizzi e vantaggi delle Closures
Incapsulamento dei dati
Di seguito è riportato un esempio di closure con annotazioni di tipo per l’incapsulamento dei dati.
1function createCounter(): () => number {
2 let count: number = 0;
3
4 return function (): number {
5 count += 1;
6 return count;
7 };
8}
9
10const counter: () => number = createCounter();
11console.log(counter()); // 1
12console.log(counter()); // 2
13console.log(counter()); // 3
- La funzione
createCounter
restituisce una funzione di tipo() => number
. - La variabile
count
è definita come tiponumber
e viene manipolata all'interno della closure.
Funzioni di ordine superiore
Le closures sono utili nella creazione di funzioni di ordine superiore. Di seguito è riportato un esempio di funzione di ordine superiore con annotazioni di tipo chiare.
1function createMultiplier(multiplier: number): (value: number) => number {
2 return function (value: number): number {
3 return value * multiplier;
4 };
5}
6
7const double: (value: number) => number = createMultiplier(2);
8console.log(double(5)); // 10
9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5)); // 15
createMultiplier
prende un argomento di tiponumber
e restituisce una funzione di tipo(value: number) => number
.- La funzione interna accetta anch'essa
value
come tiponumber
e restituisce il risultato come tiponumber
.
Esempio di implementazione delle Closures in TypeScript
Implementare un contatore con limiti di range come closure con annotazioni di tipo aggiuntive.
1function rangeCounter(min: number, max: number): () => number | string {
2 let count: number = min;
3
4 return function (): number | string {
5 if (count <= max) {
6 return count++;
7 } else {
8 return `Count has exceeded the maximum value: ${max}`;
9 }
10 };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter()); // 1
16console.log(counter()); // 2
17console.log(counter()); // 3
18console.log(counter()); // 4
19console.log(counter()); // 5
20console.log(counter()); // "Count has exceeded the maximum value: 5"
- La funzione
rangeCounter
restituisce una funzione che restituisce unnumber
o unastring
. - Nella funzione interna, se
count
superamax
, restituisce un messaggio di tipostring
; altrimenti, restituisce un valore di tiponumber
.
Precauzioni nell'uso delle Closures
Possibili memory leak dalle Closures
Le closures possono mantenere le variabili da uno scope esterno, cosa che a volte può causare memory leak. Le closures non necessarie devono essere esplicitamente rilasciate dalla memoria.
1function createLeak(): () => void {
2 // Large array consuming significant memory
3 const largeArray: string[] = new Array(1000000).fill("leak");
4
5 // Closure capturing `largeArray`
6 return function (): void {
7 console.log(largeArray[0]); // Using the captured array
8 };
9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
- In questo codice, l’array
largeArray
creato all’interno dicreateLeak
dovrebbe essere rilasciato quando esce dallo scope, ma non accade perché la closure catturalargeArray
. FinchéleakyFunction
esiste, questa memoria non necessaria continuerà ad essere trattenuta. - Quando un oggetto o una variabile non sono più necessari, impostarne il riferimento a
null
permette al garbage collector di rilevarlo e liberare la memoria.
Uso improprio delle Closures nei cicli
Quando si creano closures all'interno di un ciclo, possono sorgere problemi riferendosi alla stessa variabile. L'esempio seguente mostra un caso in cui la variabile i
non funziona correttamente.
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
Questo codice non produce il risultato desiderato perché i
si riferisce al valore 3 alla fine del ciclo. Per risolvere questo problema, usa let
per separare lo scope oppure utilizza una funzione invocata immediatamente.
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
Utilizzando let
, lo scope di i
viene separato ad ogni iterazione del ciclo, ottenendo i risultati attesi.
Riepilogo
In TypeScript, le closures possono rendere il codice più sicuro e prevedibile sfruttando il sistema di tipi. Un uso corretto delle closures consente l'incapsulamento dei dati e la progettazione flessibile di funzioni di ordine superiore. Inoltre, occorre prestare attenzione alla gestione della memoria e ai riferimenti di scope indesiderati durante l’uso delle closures.
Puoi seguire l'articolo sopra utilizzando Visual Studio Code sul nostro canale YouTube. Controlla anche il nostro canale YouTube.