6.1 Concetto di closure
I closure sono uno dei concetti più potenti e importanti in JavaScript. Permettono alle funzioni di ricordare il proprio ambiente lessicale anche dopo che sono state eseguite. In questa lezione vedremo il concetto di closure, le loro caratteristiche e vi forniremo vari esempi del loro utilizzo.
Una closure in JavaScript è una combinazione di una funzione e dell'ambiente lessicale in cui è stata dichiarata. Una closure consente a una funzione di "ricordare" e accedere alle variabili e ad altre funzioni dal suo ambito di visibilità esterno anche dopo che la funzione esterna è stata eseguita.
Proprietà principali delle closure:
- Ambiente lessicale: contesto in cui è stata dichiarata la funzione, comprese tutte le variabili disponibili al momento della dichiarazione.
- Conservazione del contesto: una funzione con closure può conservare l'accesso alle variabili dall'ambito di visibilità esterno anche dopo il completamento dell'esecuzione di questa funzione esterna.
6.2 Esempi di funzionamento delle closure
Esempio 1: Una semplice closure
In questo esempio, innerFunction() ha accesso alla variabile outerVariable dal suo ambito di visibilità esterno anche dopo il completamento dell'esecuzione di outerFunction().
function outerFunction() {
let outerVariable = 'I am from the outer function';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const closure = outerFunction();
closure(); // Stamperà: I am from the outer function
Esempio 2: Contatore utilizzando la closure
In questo esempio, la funzione contatore conserva il valore della variabile count e lo incrementa ad ogni chiamata.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // Stamperà: 1
console.log(counter()); // Stamperà: 2
console.log(counter()); // Stamperà: 3
Esempio 3: Closure in un ciclo
Le closure sono spesso utilizzate per conservare i valori delle variabili nei cicli.
In questo esempio, ogni funzione all'interno dell'array arr "ricorda" il valore della variabile i nel momento della sua creazione grazie all'ambito di visibilità del blocco let:
function createArrayWithClosures() {
let arr = [];
for (let i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
};
}
return arr;
}
const closures = createArrayWithClosures();
closures[0](); // Stamperà: 0
closures[1](); // Stamperà: 1
closures[2](); // Stamperà: 2
6.3 Scenari complessi di utilizzo delle closure
Esempio 1: Applicazione parziale di una funzione (partial application)
Le closure permettono di creare funzioni applicate parzialmente, mantenendo alcuni argomenti fissi.
In questo esempio, la funzione multiply() restituisce una funzione che moltiplica l'argomento passato b con l'argomento fisso a.
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // Stamperà: 10
console.log(double(10)); // Stamperà: 20
Esempio 2: Nascondere i dati (data hiding)
Le closure possono essere utilizzate per creare variabili e metodi privati.
In questo esempio, le variabili _name e _age sono private e accessibili solo tramite i metodi dell'oggetto:
function createPerson(name, age) {
let _name = name;
let _age = age;
return {
getName: function() {
return _name;
},
getAge: function() {
return _age;
},
setName: function(newName) {
_name = newName;
},
setAge: function(newAge) {
_age = newAge;
}
};
}
const person = createPerson('John', 30);
console.log(person.getName()); // Stamperà: John
person.setName('Jane');
console.log(person.getName()); // Stamperà: Jane
console.log(person.getAge()); // Stamperà: 30
Esempio 3: Memoizzazione
Memoizzazione è una tecnica di ottimizzazione in cui i risultati di una funzione vengono salvati per evitare calcoli ripetuti per gli stessi input.
In questo esempio, la funzione memoize() utilizza una closure per conservare una cache dei risultati calcolati della funzione fn():
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key]) {
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
function slowFunction(num) {
console.log('Computing...');
return num * 2;
}
const memoizedFunction = memoize(slowFunction);
console.log(memoizedFunction(5)); // Stamperà: Computing... 10
console.log(memoizedFunction(5)); // Stamperà: 10 (risultato preso dalla cache)
GO TO FULL VERSION