Le closure sono un pattern di programmazione in JavaScript che permette di usare le variabili di uno scope lessicale esterno all'interno di un blocco di codice annidato. JavaScript supporta le closure in modo trasparente, e spesso vengono usate senza sapere cosa siano.
// Top-level declarations are global-scope
const dozen = 12;
// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
return dozen * n;
}
Il nome closure deriva storicamente dal calcolo lambda ed è stato reso popolare da Scheme (fonte) per indicare le associazioni di variabili aperte e chiuse di una funzione.
Privacy dei dati / Incapsulamento dei dati
// Consider this function:
const saveNumber = (number) => {
// The saved value is unaccessible by the outer lexical scope.
const value = number;
// We can provide access to the primitive value with a function, but the original will never change
return () => value;
};
// Attempting to set the variable outside of its lexical scope results in an error
value = 42;
Applicazione parziale
// The arguments are applied one-by-one, using closures to arrive at the final result
function partialBuildUri(scheme) {
return function (domain) {
return function (path) {
return `${scheme}://${domain}/${path}`;
};
};
}
// A function could apply them all at once.
function buildUri(scheme, domain, path) {
return partialBuildUri(scheme)(domain)(path);
}
// Or apply a few to create a flexible system of functions for reuse.
function buildHttpsExercismUri(path) {
return partialBuildUri('https')('exercism.org');
}
In qualsiasi linguaggio di programmazione, le closure sono strettamente legate allo scope delle variabili. In JavaScript, ci sono 3 tipi di scope lessicale per le variabili:
var.{...}) usando le parole chiave let e const.In pratica, una closure si verifica spesso quando una funzione (o un blocco) usa una variabile di uno scope esterno. In JavaScript questo è supportato in modo trasparente, quindi le closure vengono spesso usate senza un riferimento esplicito.
La parola chiave var definisce una variabile con scope di funzione. Questo significa che le variabili definite da var sono disponibili ovunque nella funzione in cui sono definite e in qualsiasi scope lessicale annidato (sia di funzione che di blocco).
{
var five = 5;
}
// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;
Le parole chiave let e const definiscono variabili con scope di blocco. let definisce una variabile mutabile, il cui valore può essere assegnato più volte e che può essere definita ma non inizializzata. const definisce una variabile che deve essere inizializzata quando viene definita, e il cui valore in seguito non può cambiare. Se alla variabile const viene assegnato un valore primitivo, non può essere mutata. Se alla variabile const viene assegnato un valore di riferimento, come un array o un object, il riferimento non può cambiare, ma il contenuto dell'array può essere mutato.
Considera questo blocco:
// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;
{
// This succeeds, as 'two' is available from the outer lexical scope
const oneLess = two - 1;
}
// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);
Anche se possiamo usare le parentesi graffe ({...}) arbitrariamente per creare blocchi, il più delle volte le troviamo intorno a istruzioni if, funzioni e classi.
const PI = 3.14;
// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
return PI * radius * radius;
}