javascript Intermediate 3 min

Closures: cos'è e perché è fondamentale in JavaScript

Scope chain, lexical environment, pattern modulo, data privacy

· 658 parole

Ecco la definizione ufficiale, in una frase: una closure è una funzione racchiusa insieme (enclosed) alle variabili del suo ambiente circostante: il suo lexical environment. In pratica: una funzione "si ricorda" delle variabili che esistevano quando è stata creata, anche molto tempo dopo che quella funzione esterna ha già finito di eseguire.

1. Lexical scoping: la visibilità decisa da dove scrivi il codice

MDN è preciso su questo punto: in JavaScript, la visibilità (scope) di una variabile è definita dalla sua posizione nel codice sorgente, si vede "lessicalmente", leggendo il testo e le funzioni annidate hanno accesso alle variabili dichiarate nel loro scope esterno.

function init() { 
     var name = "Mozilla"; // variabile locale creata da init function 
     
      displayName() { 
         console.log(name); // usa la variabile del genitore 
      } 
      
      displayName(); 
} 

init(); // stampa "Mozilla"

Quando displayName cerca name, JavaScript risale la scope chain: prima guarda nello scope locale, poi in quello della funzione che la contiene, poi ancora più fuori, fino allo scope globale, finché non trova la variabile o esaurisce i livelli.

Immagina di cercare un oggetto in casa: prima guardi nella tua stanza, poi nel corridoio, poi in tutta la casa. Se non lo trovi da nessuna parte, concludi che non c'è. La funzione fa lo stesso, un livello di scope alla volta, verso l'esterno.

Fonte: MDN Web Docs: «Closures»: la visibilità di una variabile è definita dalla sua posizione nel codice sorgente; le funzioni annidate hanno accesso alle variabili dichiarate nel loro scope esterno (lexical scoping).

2. La closure in azione: la funzione che «ricorda»

La parte sorprendente arriva quando la funzione interna viene restituita dalla funzione esterna, prima di essere eseguita. A prima vista sembra impossibile che funzioni ancora, la funzione esterna è già terminata! Ma il lexical environment resta agganciato alla funzione restituita.

function creaContatore() {
  let contatore = 0; // "chiusa dentro" (enclosed) dalla funzione interna
  return function () {
    contatore++;
    return contatore;
  };
}
const conta = creaContatore();
console.log(conta());
// 1 console.log(conta());
// // 2 -- se lo ricorda!
const contaDiNuovo = creaContatore(); // un NUOVO ambiente, indipendente
console.log(contaDiNuovo()); // 1 -- non condivide nulla con "conta"

Ogni chiamata a creaContatore(), crea un nuovo lexical environment, indipendente dagli altri: conta e contaDiNuovo hanno ciascuno il proprio contatore, e modificarne uno non tocca l'altro.

Se apri un nuoco conto in banca (chiami creaContatore), ottieni un libretto tutto tuo, con il suo saldo indipendente. Due libretti aperti in momenti diversi non condividono mai lo stesso saldo, anche se sono stati creati dallo stesso sportello con la stessa procedura.

3. Il pattern modulo: closures per nascondere dati

MDN segnala esplicitamente un vantaggio delle closures che di norma associamo alla programmazione orientata agli oggetti: data hiding ed encapsulation. Puoi restituire più funzioni pubbliche che condividono lo stesso ambiente privato, invisibile dall'esterno.

function creaContoBancario(saldoIniziale) {
    let saldo = saldoIniziale; // Privato: irraggiungibile dall'esterno

    return {
        deposita(importo) {
            saldo += importo;
            return saldo;
        },

        preleva(importo) {
            if (importo > saldo) {
                throw new Error("Fondi insufficienti");
            }

            saldo -= importo;
            return saldo;
        },

        leggiSaldo() {
            return saldo;
        },
    };
}

const conto = creaContoBancario(100);

conto.deposita(50); // 150
conto.saldo; // undefined: non esiste una via diretta

Nessuno può leggere o modificare saldo direttamente: l'unico modo per interagirci è attraverso le funzioni pubbliche che tu hai deciso di esporre. Questo si chiama pattern modulo: incapsulare uno stato privato dietro un'interfaccia pubblica controllata.

In uno sportello bancomat: non puoi mettere le mani direttamente nella cassaforte della banca (il saldo), ma puoi depositare e prelevare attraverso un'interfaccia precisa che la banca ti mette a disposizione. La cassaforte resta protetta; l'interazione avviene solo dove è previsto.

In sintesi

Concetto Cosa significa In una frase
Closure Funzione + il suo lexical environment agganciato Una funzione con uno zaino di variabili
Lexical scoping La visibilità dipende da dove il codice è scritto Si legge dal testo, non dall'esecuzione
Scope chain La ricerca di una variabile risale verso l'esterno Prima qui, poi fuori, poi ancora più fuori
Pattern modulo Stato privato + funzioni pubbliche che lo gestiscono Bancomat: la cassaforte resta protetta

Le closures sono il meccanismo che rende possibili contatori indipendenti, dati privati, e gran parte dei pattern che userai ogni giorno scrivendo JavaScript, anche quando non ci pensi.

Per approfondire

Accedi o registrati per votare, salvare o commentare la pillola

Commenti (0)

Registrati per commentare, fare domande e proporre l'argomento della prossima pillola (oppure accedi se hai già un account).

Nessun commento. Sii il primo a scrivere qualcosa.

Pillole correlate