Come funzionano le chiusure di JavaScript, in parole povere
JavaScript è un linguaggio ampiamente adottato che si può usare per costruire qualsiasi cosa, da una semplice landing page a un’applicazione full-stack di produzione. Con l’evoluzione di JavaScript e della programmazione in generale, gli sviluppatori si sono resi conto che il paradigma della programmazione orientata agli oggetti (OOP) è indesiderabile per la maggior parte dei casi d’uso. La programmazione funzionale è emersa come la soluzione a molti dei punti dolenti associati all’OOP.
Le chiusure sono un argomento ampiamente discusso nel mondo della programmazione funzionale, ma sono spesso definite in modo vago e in gergo tecnico. Qui faremo del nostro meglio per spiegare come funzionano le chiusure JavaScript in termini profani.
Alla fine di questo tutorial, dovreste capire:
- Come identificare le chiusure
- Cos’è una chiusura e come si comporta in relazione al contesto di esecuzione e allo stack delle chiamate
- Casi d’uso comuni per le chiusure
Comprendere le chiusure JavaScript
Iniziamo mostrando come appare una chiusura.
Prova ad eseguire il codice da solo. Tecnicamente, la funzione chiamata makeCounter
restituisce un’altra funzione chiamata increment
. Questa funzione increment
ha accesso alla variabile count
anche dopo che la funzione makeCount
è stata eseguita. Parte della chiusura qui è la variabile count
; è disponibile alla funzione increment
quando è definita, anche dopo che makeCounter
ha terminato. L’altra parte è la funzione increment
.
Immaginate di avere una casa e un giardino che la circonda. Una volta che aprite la porta del giardino e la chiudete, non potete aprirla di nuovo – il giardino diventa inaccessibile. Hai fame e, fortunatamente, ci sono un albero di arance e un albero di mele nel tuo giardino. Prendi una piccola borsa, raccogli un’arancia e una mela e torni in casa. Ricorda, non puoi tornare fuori di nuovo.
Ora, una volta che sei dentro casa, puoi prendere l’arancia o la mela dal sacchetto e mangiarla ogni volta che hai di nuovo fame. Il sacchetto piccolo in questo esempio è la chiusura. Una chiusura contiene tutte le variabili e le funzioni che erano a vostra disposizione quando eravate in giardino, anche quando siete dentro casa e non potete uscire di nuovo.
Vediamo come funziona nel codice:
Siccome la variabile fruits
è disponibile alla funzione restituita quando makeFruitGarden
viene eseguito, la variabile fruits
e la funzione interna diventano la chiusura. Ogni volta che consumeFruit
viene eseguito, viene restituito un fruit
– l’ultimo elemento dell’array fruits
perché pop()
è in uso. Una volta che entrambi i frutti sono stati consumati, non ci sarà più nulla da mangiare.
Comprendere lo scope lessicale
Per capire veramente le chiusure, dovreste avere familiarità con il termine “scope”. Lo scope lessicale è un termine di fantasia per indicare l’ambiente corrente relativo a qualsiasi cosa a cui ci si riferisce.
Nell’esempio seguente, lo scope della variabile chiamata myName
è chiamato “scope globale”.
// global scopeconst myName = "John Doe"function displayName() { // local/function scope console.log(myName);};displayName()
Potreste aver visto questo concetto di cui si parla quando si legge di come var
non sia block-scoped e come const
let
lo sia. È importante notare che in JavaScript, una funzione crea sempre il proprio ambito. Questo è chiamato local
o function
scope, come mostrato nell’esempio di codice.
Se avete fatto attenzione, potreste pensare che myName
e displayName
sono parte di una chiusura. Avreste ragione! Ma dato che la funzione e la variabile qui esistono nello scope globale, non c’è molto valore nel chiamarla chiusura.
Ci sono molti tipi di scope in JavaScript, ma quando si tratta di chiusure, ci sono tre scope che dovreste conoscere:
- Lo scope globale è lo scope di default dove tutti vivono. Pensatelo come la vostra strada
- Lo scope esterno della funzione è la funzione che restituisce una funzione. Ha il proprio scope. Pensate al vostro giardino
- L’ambito funzionale interno/locale è la funzione restituita che diventa una chiusura. Pensatelo come la vostra casa
Ora immergiamoci in alcuni casi d’uso.
Casi d’uso comuni per le chiusure
Currying
Function currying è un altro potente concetto nella programmazione funzionale. Per implementare una funzione curry in JavaScript, si usano le chiusure.
Curare una funzione può essere descritto come trasformare una funzione e viene eseguito in questo modo: add(1, 2, 3)
a add(1)(2)(3)
.
function add(a) { return function(b) { return function(c) { return a + b + c; }; };};add(1)(2)(3) // returns 6
La funzione add
prende un singolo argomento e poi restituisce due funzioni che sono annidate una dopo l’altra. L’obiettivo del currying è di prendere un mucchio di argomenti e alla fine finire con un singolo valore.
Funzioni di ordine superiore
L’obiettivo di una funzione di ordine superiore è di prendere una funzione come argomento e restituire un risultato. I metodi Array come map
e reduce
sono esempi di funzioni di ordine superiore.
const arrayOfNumbers = ;const displayNumber = (num) => { console.log(num);}arrayOfNumbers.forEach(displayNumber)
La Array.prototype.forEach
funzione di ordine superiore qui accetta displayNumber
come argomento e poi la esegue per ogni elemento nel arrayOfNumbers
. Se avete usato un framework UI come Vue o React, potreste avere familiarità con i componenti di ordine superiore, che sono essenzialmente la stessa cosa delle funzioni di ordine superiore.
Quindi qual è la differenza tra funzioni di ordine superiore e currying? Mentre una funzione di ordine superiore prende una funzione come argomento e restituisce un valore, una funzione curry restituisce una funzione come risultato, che alla fine porta ad un valore.
Gestori di elementi DOM
Questo è un comune design pattern spesso usato per ottenere e impostare proprietà di elementi DOM. Nell’esempio seguente, faremo un element manager per dare stile agli elementi.
makeStyleManager
restituisce un oggetto che dà accesso a due funzioni, che fanno parte di una chiusura insieme alle variabili element
e currentStyles
. Anche dopo che makeStyleManager
ha terminato l’esecuzione, le funzioni getStyle
e setStyle
hanno accesso alle variabili.
Conclusione
Le chiusure JavaScript possono essere difficili da capire, anche per sviluppatori con esperienza professionale. Capire le chiusure vi renderà in definitiva uno sviluppatore migliore.
Ora dovreste essere in grado di identificare una chiusura quando viene usata in un codice che sembra strano o non ha senso. Le chiusure sono un concetto critico nella programmazione funzionale e spero che questa guida vi abbia aiutato a fare un passo avanti nel vostro viaggio verso la sua padronanza.
LogRocket: Debuggare gli errori JavaScript più facilmente comprendendo il contesto
Il debug del codice è sempre un compito noioso. Ma più si capiscono gli errori, più facile è correggerli.
LogRocket vi permette di capire questi errori in modi nuovi e unici. La nostra soluzione di monitoraggio frontend traccia l’impegno dell’utente con i vostri frontend JavaScript per darvi la possibilità di scoprire esattamente cosa ha fatto l’utente che ha portato ad un errore.
LogRocket registra i log della console, i tempi di caricamento della pagina, stacktraces, richieste/risposte di rete lente con intestazioni + corpi, metadati del browser e registri personalizzati. Capire l’impatto del tuo codice JavaScript non sarà mai stato così facile!
Provalo gratuitamente.
Altre letture
- “Non ho mai capito le chiusure JavaScript” – Una guida tecnica approfondita per capire come funziona una chiusura. Ideale per chi vuole capire le chiusure rispetto al
ExecutionContext
, allo stack delle chiamate, all’ambiente lessicale, ecc. - Riferimento MDN per le chiusure JavaScript – Una guida rapida alle chiusure (incluse alcune insidie comuni) nel caso abbiate bisogno di rispolverare velocemente la vostra memoria
- “Master the JavaScript Interview: Cos’è una chiusura? – Le chiusure spiegate da Eric Elliot; ottimo per gli sviluppatori che hanno una comprensione dei concetti di programmazione funzionale