W3docs

Closure

Impara le closure in JavaScript: come una funzione ricorda il suo scope lessicale, con esempi su privacy dei dati, factory di funzioni, il pattern modulo e i problemi nei loop.

Comprendere le Closure in JavaScript

In JavaScript, le closure sono un concetto fondamentale e potente che consente di creare funzioni con dati preservati dal loro ambiente lessicale. Questa guida fornirà un'esplorazione approfondita delle closure, con spiegazioni dettagliate ed esempi pratici di codice per consolidare la comprensione.

Cos'è una Closure?

Una closure è la combinazione di una funzione e dell'ambiente lessicale all'interno del quale quella funzione è stata dichiarata. Questo ambiente è composto da tutte le variabili locali che erano nello scope al momento in cui la closure è stata creata.

Esempio di Closure

Considera il seguente esempio:

javascript— editable

In questo esempio, greetByName è una closure. Cattura le variabili greeting e name dallo scope della funzione greet.

Quando la funzione greet viene chiamata con l'argomento 'John', crea una variabile locale greeting e una funzione greetByName. La funzione greet restituisce quindi la funzione greetByName. Anche dopo che la funzione greet ha terminato l'esecuzione, la funzione greetByName restituita ha ancora accesso alle variabili greeting e name. Questo avviene perché greetByName è una closure, il che significa che ricorda l'ambiente in cui è stata creata.

Perché Usare le Closure?

Le closure consentono l'incapsulamento dei dati e la creazione di variabili private. Permettono alle funzioni di mantenere l'accesso alle variabili del proprio scope contenitore (o "esterno") anche dopo che tale scope ha terminato l'esecuzione.

Creare una Closure di Base

Considera il seguente esempio:

javascript— editable

Quando outerFunction viene invocata, crea outerVariable e innerFunction. Restituisce quindi innerFunction. La funzione innerFunction restituita mantiene l'accesso a outerVariable anche dopo che outerFunction ha terminato l'esecuzione.

Casi d'Uso Pratici delle Closure

Privacy dei Dati

Le closure possono essere utilizzate per creare dati privati non accessibili dallo scope globale.

javascript— editable

In questo esempio, count è una variabile privata accessibile e modificabile solo dalla funzione anonima restituita. Ogni volta che la funzione viene chiamata, incrementa e restituisce il count aggiornato.

Un punto sottile ma importante: ogni chiamata a createCounter produce la propria closure indipendente con il proprio count privato. Due contatori creati separatamente non condividono lo stato:

javascript— editable

Ogni invocazione della funzione esterna crea un ambiente lessicale completamente nuovo, quindi a e b incrementano variabili separate.

Factory di Funzioni

Le closure possono essere utilizzate per creare factory di funzioni che generano funzioni specializzate.

javascript— editable

Qui, createMultiplier genera funzioni che moltiplicano un dato numero per un multiplier specificato. Ogni funzione restituita mantiene l'accesso al valore di multiplier tramite la closure.

Closure nei Loop

Le closure possono essere complicate quando vengono usate all'interno dei loop. Considera il seguente esempio:

javascript— editable

In questo codice, il loop si completa e poi tutti e tre i callback di setTimeout vengono eseguiti, stampando il valore di i, che è 4 dopo la fine del loop. Questo accade perché var ha scope di funzione.

Per risolvere questo problema, usa let, che ha scope di blocco:

javascript— editable
Informazione

Usa sempre let o const al posto di var per evitare problemi di scope e garantire che le variabili abbiano lo scope appropriato.

Perché la versione con let funziona

Il punto chiave è che let crea un nuovo binding di i per ogni iterazione del loop. Ogni callback di setTimeout chiude su un i diverso, quindi ricorda il valore che esisteva durante la propria iterazione. Con var, esiste un unico i condiviso da tutti i callback, e nel momento in cui i timer scattano il loop lo ha già portato a 4.

Prima che let esistesse, la soluzione classica era creare un nuovo scope per ogni iterazione tramite una IIFE:

javascript— editable

La funzione invocata immediatamente copia il valore corrente di i nel proprio parametro captured, fornendo a ciascun callback una copia privata su cui formare la closure.

Closure e Gestione della Memoria

Le closure possono causare perdite di memoria se non vengono utilizzate correttamente. Poiché le closure mantengono riferimenti al proprio scope esterno, le variabili inutilizzate possono persistere in memoria più a lungo del necessario.

Attenzione

Presta attenzione all'utilizzo della memoria quando crei closure. Assicurati che le closure non catturino inutilmente oggetti di grandi dimensioni o elementi DOM per evitare perdite di memoria.

Pattern Avanzati con le Closure

Pattern Modulo

Il pattern modulo sfrutta le closure per creare membri privati e pubblici all'interno di una funzione.

javascript— editable

In questo esempio, CounterModule è un'espressione di funzione invocata immediatamente (IIFE) che restituisce un object con metodi pubblici (increment e reset). La variabile count rimane privata e non può essere accessibile direttamente.

Closure nei Framework JavaScript Moderni

Le closure sono particolarmente importanti nei framework JavaScript moderni come React. In React, le closure aiutano a gestire lo stato e gli effetti collaterali all'interno dei componenti funzionali.

import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    function increment() {
        setCount(prevCount => prevCount + 1);
    }

    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
}

In questo esempio, useState viene utilizzato per creare una variabile di stato count e una funzione setCount per aggiornarla. La funzione increment forma una closure, catturando setCount e count dallo scope del componente. Nota: in React, le closure possono talvolta catturare uno stato obsoleto se le dipendenze non vengono gestite correttamente negli hook come useEffect.

Informazione

Esercitati a creare e usare le closure in contesti diversi per cogliere appieno il loro potenziale e i loro limiti. Sono uno strumento essenziale nella cassetta degli attrezzi di qualsiasi sviluppatore JavaScript.

Best Practice per l'Utilizzo delle Closure

  1. Limita lo Scope: Evita di creare closure all'interno di loop o strutture profondamente annidate se non necessario, per prevenire perdite di memoria e problemi di prestazioni.
  2. Minimizza le Variabili Catturate: Cattura nella closure solo le variabili di cui hai bisogno per ridurre il consumo di memoria.
  3. Evita Catture di Grandi Dimensioni: Non catturare oggetti di grandi dimensioni o elementi DOM nelle closure per prevenire un'inutile ritenzione di memoria.
  4. Usa let e const: Preferisci sempre let o const rispetto a var per garantire uno scope corretto ed evitare comportamenti indesiderati.
  5. Pulizia: Dove possibile, elimina i riferimenti catturati dalle closure per evitare perdite di memoria, specialmente nelle applicazioni a lunga esecuzione.

Conclusione

Le closure sono una funzionalità potente in JavaScript, che abilita la privacy dei dati, le factory di funzioni e pattern avanzati come i moduli. Comprendere e utilizzare efficacemente le closure può portare a un codice più robusto e manutenibile. Padroneggiando le closure, migliorerai la tua capacità di scrivere codice JavaScript efficiente, sicuro e pulito.

Esercitazione

Pratica
Cosa sono le closure in JavaScript?
Cosa sono le closure in JavaScript?
Pratica
Perché un loop 'for' che usa 'var' con setTimeout stampa lo stesso valore finale tre volte, mentre 'let' stampa 1, 2, 3?
Perché un loop 'for' che usa 'var' con setTimeout stampa lo stesso valore finale tre volte, mentre 'let' stampa 1, 2, 3?
Was this page helpful?