Retour à l'accueil

Fermetures JS : Environnement lexical et exemples

Les fermetures en JavaScript permettent à la fonction interne d'accéder aux variables externes après l'achèvement de la fonction externe. Le matériel couvre la théorie du calcul lambda, les pratiques dans les boucles, l'émulation OOP et l'optimisation du moteur. Exemples de code pour développeurs middle/senior.

Comment fonctionnent les fermetures en JS : Code et pièges des boucles
Advertisement 728x90

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 :

Google AdInline article slot
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 :

Google AdInline article slot
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 :

Google AdInline article slot
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/const dans 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.
  • let dans les boucles crée des fermetures par itération, var ne 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

Advertisement 728x90

Lire ensuite