Les Fermetures JavaScript : Mécanisme d'Environnement Lexical et Applications Pratiques
Une fermeture en JavaScript est une combinaison d'une fonction et de son environnement lexical, où une fonction interne conserve l'accès aux variables d'une portée externe même après que la fonction externe ait terminé son exécution. Cela permet l'encapsulation d'état et un comportement prévisible dans des contextes asynchrones.
Le concept trouve son origine dans le lambda-calcul d'Alonzo Church. Les fonctions d'ordre supérieur retournent ou acceptent d'autres fonctions, capturant les variables libres — celles utilisées à l'intérieur mais déclarées à l'extérieur.
Un exemple basique de compteur illustre le principe :
function counter() {
let count = 0
return function(step) {
count = count + step
return count
}
}
const myCounter = counter()
console.log(myCounter(1)) // 1
console.log(myCounter(1)) // 2
console.log(myCounter(1)) // 3
La fonction interne se ferme sur count, empêchant le ramasse-miettes de le libérer. Il s'agit d'une référence vivante : les modifications sont reflétées en temps réel.
Les Fermetures comme Analogue à la POO
Les fermetures imitent les champs privés et les API publiques des objets. Un compteur avec des méthodes :
function Counter(initialValue = 0) {
let count = initialValue
return {
increment: function(step = 1) {
return count += step
},
decrement: function(step = 1) {
return count -= step
},
getValue: function() {
return count
},
reset: function() {
return count = initialValue
}
}
}
const myCounter = Counter(10)
console.log(myCounter.getValue()) // 10
console.log(myCounter.increment(5)) // 15
Un équivalent utilisant des classes avec des champs privés (#count) montre une logique identique. Dans les stores Pinia/Vuex, les fermetures encapsulent l'état réactif :
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const initValue = 0
function increment(step) {
count.value += step
}
return { count, increment }
})
Nuances dans les Boucles et Itérateurs
Le comportement des fermetures dans les boucles dépend du hoisting et de la portée de bloc.
Avec var — une variable partagée pour toutes les itérations :
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 3, 3, 3
}, 100)
}
Avec let — un environnement lexical séparé par itération :
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 0, 1, 2
}, 100)
}
Les itérateurs comme forEach, map, filter créent des fermetures automatiquement :
[0, 1, 2].forEach(function(i) {
setTimeout(function() {
console.log(i) // 0, 1, 2
}, 100)
})
Pratique : Callbacks et Événements
Dans le code asynchrone, les fermetures résolvent les problèmes de contexte.
Requête Ajax avec un ID unique :
function ajaxRequest(url) {
let requestId = Math.random().toString(36).substring(2)
fetch(url).then(data => {
console.log(`Requête ${requestId} terminée :`, data)
}).catch(error => {
console.log(`Requête ${requestId} échouée :`, error.message)
})
}
Gestionnaires d'événements :
function clickSetup(elemId, message) {
const elem = document.getElementById(elemId)
elem.addEventListener('click', function() {
console.log(message)
})
}
Les fonctions fléchées fonctionnent de manière identique aux fonctions régulières.
Optimisation et Débogage
Les moteurs JS (V8) excluent les variables inutilisées des fermetures pour économiser la mémoire. Dans DevTools (Scope → Closure), seules les variables pertinentes sont visibles.
Pratiques clés :
- Utilisez
let/constdans les boucles pour la portée de bloc. - Préférez les fermetures pour l'état privé sans classes.
- Dans les callbacks asynchrones, fixez le contexte tôt.
- Surveillez la mémoire dans les fermetures de longue durée.
Points Clés à Retenir
- Fermeture = fonction + environnement lexical avec références vivantes aux variables.
letdans les boucles crée des fermetures par itération,varne le fait pas.- Imite les champs privés de la POO, utilisé dans les stores Vuex/Pinia.
- Optimisé par les moteurs : les variables inutilisées sont exclues.
- Essentiel pour les callbacks asynchrones et les événements.
— Editorial Team
Aucun commentaire pour le moment.