Bienvenue sur Développeur Pro ! Si vous êtes nouveau ici, vous voudrez sans doute transformer une carrière en développeur professionnel avec ce guide " Le kit développeur pro™ " : cliquez ici pour télécharger le guide gratuitement ! 🙂
Bienvenue à nouveau sur Développeur Pro ! Comme ce n'est pas la 1ère fois que vous venez ici, vous voudrez sans doute transformer votre carrière de développeur avec " Le kit développeur pro™ " : cliquez ici pour télécharger le guide gratuitement ! 🙂
Les Fonctions de Closures en JavaScript : (Closure Function)
Les Closures sont l’un des concepts les plus intéressants et les plus subtils en JavaScript.
Néanmoins Elles peuvent parfois sembler complexes à comprendre et dans quel contexte les utiliser.
Voici un article qui va déstructurer enfin ce concept à savoir :
Que ce qu’est une closure, comment elle fonctionne, et comment l’utiliser efficacement.
Qu’est-ce qu’une Closure ?
Une closure est une fonction qui capture et conserve les variables de son environnement lexical au moment où elle est créée.
En clair, une closure « se souvient » du contexte dans lequel elle a été définie, même si elle est exécutée en dehors de ce contexte.
Cependant on utilise une closure avec une fonction anonyme à l’intérieur d’une fonction nommée (voir articles sur les fonctions).
Tout d’abord Le Contexte Lexical
Pour bien comprendre les closures, il est crucial de comprendre ce qu’est un contexte lexical dans une fonction en Javascript.
Autrement dit en JavaScript, le contexte lexical fait référence à la manière dont les variables et les fonctions sont organisées et accessibles dans le code en fonction de leur emplacement (leur position) dans le code source.
Par exemple lorsque vous définissez une fonction dans JavaScript, elle capture le contexte lexical où elle a été créée, c’est-à-dire toutes les variables et fonctions accessibles à ce moment-là.
Ce contexte reste attaché à la fonction même lorsqu’elle est exécutée en dehors de sa portée d’origine.
Exemple de Closure en Action
Voyons un exemple simple pour illustrer cela :
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter1 = createCounter();
console.log(counter1()); // Affiche 1
console.log(counter1()); // Affiche 2
console.log(counter1()); // Affiche 3
Explication du code ci dessus :
-
Définition de
createCounter: La fonctioncreateCountercrée une variable localecountet retourne une fonction anonyme. -
Création de la Closure : Lorsque
createCounterest appelée, elle retourne une nouvelle fonction qui a accès à la variablecountdéfinie danscreateCounter. Cette fonction anonyme est la closure. -
Maintien de l’État : Même après que
createCountera terminé son exécution, la fonction retournée (la closure) conserve une référence à la variablecount. À chaque appel decounter1(), la variablecountest incrémentée.
Maintenant Pourquoi les Closures Sont-elles Utiles ?
Les closures offrent plusieurs avantages dans la programmation JavaScript :
-
Encapsulation : Elles permettent de cacher l’état interne d’une fonction. Par exemple, dans l’exemple précédent, la variable
countn’est pas accessible directement depuis l’extérieur, ce qui garantit que l’état ne peut être modifié qu’à travers la closure. -
Fonctions d’usine (Factory Functions) : Les closures sont couramment utilisées pour créer des fonctions d’usine qui génèrent des fonctions personnalisées selon des paramètres.
-
Fonctions Callback : Elles sont très utilisées dans les callbacks et les gestionnaires d’événements pour capturer l’état au moment où le callback est défini.
-
Gestion de l’état : Elles permettent de maintenir un état persistant dans des environnements où cela pourrait autrement être difficile, comme dans les boucles ou les gestionnaires d’événements.
Voici des Exemples plus Avancés de Closures
Exemple 1 : Utiliser des Closures pour Capturer des Variables de Boucle
Les closures peuvent être utilisées pour capturer l’état d’une variable dans une boucle en JavaScript. Sans closure, cela peut conduire à des comportements inattendus.
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
// Cela affichera 6 cinq fois, car `i` est capturé par référence et est égal à 6 après la boucle.
i à chaque itération :
for (var i = 1; i <= 5; i++) {
(function(x) {
setTimeout(function() {
console.log(x);
}, x * 1000);
})(i);
}
// Cela affichera 1, 2, 3, 4, 5 respectivement.
Exemple 2 : Créer des Fonctions d’usine avec des Closures
Les fonctions d’usine sont des fonctions qui retournent d’autres fonctions configurées de manière spécifique. Les closures sont parfaites pour cela.
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const doubler = createMultiplier(2);
const tripler = createMultiplier(3);
console.log(doubler(5)); // Affiche 10
console.log(tripler(5)); // Affiche 15
createMultiplier utilise une closure pour capturer la valeur de factor. Les fonctions doubler et tripler « se souviennent » du facteur avec lequel elles ont été créées.Voici un Exemple sans Closure et les Erreurs Générées
Imaginons que nous ayons un scénario où nous voulons créer plusieurs boutons sur une page web, et que lorsque nous cliquons sur un bouton, il affiche son index (c’est-à-dire sa position dans la liste des boutons).
Sans Closure : Utilisation d’une Variable Globale
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exemple Sans Closure</title>
</head>
<body>
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
<script>
var buttons = document.querySelectorAll('button');
for (var index = 0; index < buttons.length; index++) {
buttons[index].addEventListener('click', function() {
console.log("Button index : " + index);
});
}
</script>
</body>
</html>
Explication
- Variable Globale
index: Ici, nous avons utilisé une variable globaleindexpour stocker l’index du bouton. - Problème : Lorsque vous cliquez sur n’importe quel bouton, vous verrez toujours la même valeur affichée. Ce comportement se produit parce que la variable globale
indexest partagée entre toutes les fonctions de gestion d’événements, et lorsque la boucleforse termine, la valeur deindexest celle du dernier index de la boucle (dans ce cas,3, carindexest incrémenté après la dernière itération).
Effectivement Ce Résultat est Une Erreur Logique
Si vous cliquez sur n’importe quel bouton, vous verrez Button index : 3 pour tous les boutons, ce qui est incorrect.
Donc l’erreur est due au fait que toutes les fonctions d’événements se réfèrent à la même variable globale index, qui a été modifiée lors de l’exécution de la boucle.
Avec une Closure : Nous Résolvons ce Problème
Maintenant, voyons comment faire pour utiliser une closure pour résoudre ce problème :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exemple Avec Closure</title>
</head>
<body>
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
<script>
var buttons = document.querySelectorAll('button');
for (var index = 0; index < buttons.length; index++) {
(function(i) {
buttons[i].addEventListener('click', function() {
console.log("Button a index : " + i);
});
})(index);
}
</script>
</body>
</html>
Explication
- Fonction Anonyme Immédiatement Invoquée (IIFE) : Nous avons utilisé une IIFE (Immediately Invoked Function Expression) pour créer une closure. Cette fonction est exécutée immédiatement et capture la valeur de
indexpour chaque itération. - Variable
i: La variableiest passée en argument à l’IIFE, ce qui permet de capturer la valeur actuelle deindexà chaque tour de boucle.
Le Résultat est un Comportement Correct
Maintenant, si vous cliquez sur les boutons, vous verrez :
Button index : 0pour le premier bouton,Button index : 1pour le deuxième bouton,Button index : 2pour le troisième bouton.
Cependant autre méthode sans var
En utilisant let au lieu de var (voir article sur les variables en JavaScript) pour déclarer la variable d’index dans la boucle, vous pouvez résoudre ce problème sans avoir besoin d’utiliser de closures.
C’est parce que let a une portée de bloc (block scope), contrairement à var qui a une portée de fonction (function scope).
Exemple avec let au lieu de var
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exemple avec let</title>
</head>
<body>
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
<script>
var buttons = document.querySelectorAll('button');
for (let index = 0; index < buttons.length; index++) {
buttons[index].addEventListener('click', function() {
console.log("Button index : " + index);
});
}
</script>
</body>
</html>
Explication
- Portée de Bloc avec
let: Lorsque vous utilisezlet, la variableindexest réinitialisée à chaque itération de la boucle. Cela signifie que chaque gestionnaire d’événements se voit attribuer une nouvelle instance deindexqui est spécifique à cette itération de la boucle.
Le Comportement est Correct
Tout comme avec la solution utilisant une closure, si vous cliquez sur les boutons, vous verrez :
Button index : 0pour le premier bouton,Button index : 1pour le deuxième bouton,Button index : 2pour le troisième bouton.
let dans une boucle for est une solution simple pour le problème de portée des variables sans avoir besoin de recourir aux closures.let, chaque itération de la boucle dispose de sa propre instance de la variable, ce qui résout le problème des références incorrectes dans les gestionnaires d’événements.Bien que les closures soient extrêmement nécessaires dans de nombreux autres cas, dans ce cas précis, l’utilisation de let offre une solution plus directe à ce problème.
Dans Quelle autre Cas ?
Les closures sont utiles pour plusieurs raisons, même lorsque des alternatives comme l’utilisation de let sont disponibles pour certains cas spécifiques.
Comme en témoignent les utilisations des closures, elles offrent des capacités uniques qui vont bien au-delà de la simple gestion de la portée des variables dans des boucles.
Voici pourquoi les closures restent essentielles et des situations où elles sont particulièrement utiles :
1. Maintien de l’État Privé
Les closures permettent de créer des fonctions avec un état privé qui persiste entre les appels.
Contrairement à des variables globales ou à portée de bloc, cet état est encapsulé dans la fonction et ne peut être modifié que par cette fonction.
Exemple : Compteur avec État Privé
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter1 = createCounter();
console.log(counter1()); // Affiche 1
console.log(counter1()); // Affiche 2
console.log(counter1()); // Affiche 3
const counter2 = createCounter();
console.log(counter2()); // Affiche 1 (compteur distinct)
count est encapsulé dans la closure, ce qui permet à counter1 et counter2 de maintenir leur propre état indépendamment l’un de l’autre. Cet état ne peut pas être modifié directement depuis l’extérieur, ce qui renforce l’encapsulation.2. Création de Fonctions Dynamiques
Les closures permettent de créer des fonctions qui sont configurées dynamiquement en fonction des variables d’un contexte externe.
Exemple : Fonctions Générées Dynamiquement
function createAdder(x) {
return function(y) {
return x + y;
};
}
const add5 = createAdder(5);
const add10 = createAdder(10);
console.log(add5(2)); // Affiche 7
console.log(add10(2)); // Affiche 12
Preuve d’utilité : La closure ici capture la valeur de x à chaque fois que createAdder est appelée, créant ainsi des fonctions spécifiques (add5, add10) qui ajoutent un nombre prédéfini à un autre nombre. Cette flexibilité est difficile à obtenir sans closures.
3. Callbacks et Gestion d’Événements
Dans les environnements asynchrones, comme avec les callbacks ou les gestionnaires d’événements, les closures permettent de capturer l’état au moment où le callback est défini.
Exemple : Gestion d’Événements avec Capture d’État
function setupButton() {
let name = "Button 1";
document.querySelector("button").addEventListener("click", function() {
alert("Clicked by " + name);
});
}
setupButton();
Preuve d’utilité : Le gestionnaire d’événements capturé « se souvient » de la valeur de name au moment où setupButton a été exécuté. Cette capture est essentielle pour que le comportement soit correct, même si name était modifié ou réinitialisé par la suite.
4. Fonctions d’Ordre Supérieur
Les closures sont à la base de nombreuses fonctions d’ordre supérieur en Javascript voir cette article (fonctions qui prennent d’autres fonctions comme arguments ou retournent des fonctions), un concept clé en programmation fonctionnelle.
Exemple : Utilisation dans map, filter, reduce
const numbers = [1, 2, 3, 4, 5];
const doubles = numbers.map(function(n) {
return n * 2;
});
console.log(doubles); // Affiche [2, 4, 6, 8, 10]
map capture l’état de chaque élément du tableau numbers. Cette capture permet de transformer chaque élément de manière concise et efficace.Se souvenir que :
En somme les closures sont extrêmement utiles pour encapsuler l’état, créer des fonctions dynamiques, gérer des callbacks, et pour des cas avancés de programmation fonctionnelle.
Ainsi elles ne sont pas seulement une solution à un problème de portée de variables dans les boucles (un problème que let peut résoudre), mais une caractéristique fondamentale qui permet une programmation plus sécurisé, découpé, rèutllisable et fonctionnelle.
Notamment leur Preuve de leur utilité :
C’est pourquoi les closures permettent des structures de code qui seraient autrement impossibles ou très compliquées à réaliser.
Par conséquent elles sont importantes pour gérer l’état privé, pour créer des fonctions d’usine, pour assurer des comportements corrects dans des contextes asynchrones, et pour écrire des fonctions d’ordre supérieur, qui sont des piliers de la programmation moderne en JavaScript.
Améliore ton bagage dans l’apprentissage de JavaScript pour cela :
Je t’offre un Guide Bonus Exclusif
En allant plus loin, avec ce Guide Bonus Exclusif rien que pour Toi !
Voici un guide complet le Kit JavaScript Pro Incubator™, où tu verras des techniques pour performer en programmation Js.
Ce guide te permettra de perfectionner tes compétences et de devenir un expert JavaScript. Ne le rate pas et développe ton expertise !
En adoptant ce qu’il contient, tu rends ton apprentissage de JavaScript plus performant avec une plus grande facilité tous les jours . Voici de quoi enrichir tout de suite ton savoir-faire avec ce guide complet qui t’attends !
Quelques liens en supplément de cette article
Voici ma Chaîne YouTube sur la programmation et le métier de développeur : https://www.youtube.com/@Developpeur-Pro
Voici un Canal ou je partage sur LinkedIn des informations sur le développement : https://www.linkedin.com/company/developpeur-pro
Retrouve ici de nombreux articles sur le code et le métier de développeur : https://developpeur-pro.com/articles-developpeur


