Maîtrisez la capture de variables par les closures pour encapsuler de l'état et éviter les pièges de portée.
Ouvrir cette leçon dans KodokonUne closure n'est pas une astuce exotique : c'est le mécanisme par lequel une fonction conserve un accès à la portée lexicale dans laquelle elle a été définie, et non celle où elle est appelée. Le moteur ne copie pas les valeurs : il garde une référence vivante vers l'environnement lexical. Deux appels à la même fabrique produisent donc deux environnements indépendants, ce qui fait des closures l'outil d'encapsulation le plus léger du langage.
function createCounter() {
let count = 0;
return {
increment: () => ++count,
reset: () => { count = 0; },
};
}
const counterA = createCounter();
const counterB = createCounter();
counterA.increment();
counterA.increment();
console.log(counterA.increment()); // 3
console.log(counterB.increment()); // 1Le piège historique du métier : var est rattaché à la fonction englobante, pas au bloc. Dans une boucle, toutes les closures capturent alors la même variable. Avec let, chaque itération crée un nouvel environnement lexical, donc une capture distincte. Ajoutez à cela la zone morte temporelle (TDZ) : une variable let ou const existe dès l'entrée dans le bloc mais lève une ReferenceError si vous la lisez avant sa déclaration.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // 3, 3, 3
}
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0); // 0, 1, 2
}Côté patterns, la closure est le socle de la mémoïsation et du module pattern. Comparée à une classe, elle offre une confidentialité réelle sans this ni prototype, au prix d'une consommation mémoire légèrement supérieure quand vous créez des milliers d'instances : chaque instance porte ses propres fonctions.
function memoize(fn) {
const cache = new Map();
return function (key) {
if (cache.has(key)) return cache.get(key);
const result = fn(key);
cache.set(key, result);
return result;
};
}
const square = (n) => n * n;
const fastSquare = memoize(square);
console.log(fastSquare(9)); // calculé
console.log(fastSquare(9)); // lu dans le cachefor (var i = ...), que journalisent trois callbacks setTimeout créés dans le corps de la boucle ?createCounter garantit-il que count reste privé ?