Qu’est ce que les closures en javascript ?

Si vous écrivez du code en JavaScript, il est fort probable que vous ayez rencontré le terme closure, qui est un concept utile mais souvent confus. Mais qu’est-ce qu’une closure ?

Une closure peut être décrite comme une combinaison d’une fonction et de l’environnement lexical dans lequel elle a été déclarée.

Mais qu’est-ce que cela signifie exactement ? L’environnement lexical est constitué de toutes les variables locales dans le champ d’application de la fonction lorsque celle-ci est créée. Une closure permet de se référer à toutes les variables locales d’une fonction dans l’état où elles ont été trouvées. Ceci est réalisé essentiellement en définissant une fonction à l’intérieur d’une autre fonction, cette fonction à l’intérieur d’une fonction est techniquement la closure. Chaque fois que la fonction parent est appelée, un nouveau contexte d’exécution est créé contenant une nouvelle copie de toutes les variables locales. Ces variables locales peuvent être référencées dans le champ d’application global en les reliant aux variables déclarées globalement ou en renvoyant la clôture depuis la fonction parent.

Un exemple simple prendra un format similaire à celui-ci :

function closuredFunc (){
    function closure(){
    // some logic
    }
}

Il est également possible d’avoir une closure qui renvoie plusieurs méthodes comme indiqué ci-dessous :

function closure(){
    function first() { console.log('I was declared first')}
    function second() { console.log('I was declared second')}
    function third() { console.log('I was declared third')}
    return [first, second, third]
}

Pour référencer chacune de ces méthodes, nous allons assigner notre closure à une variable globale qui pointera ensuite vers un tableau de méthodes exposées. Chaque méthode peut ensuite être assignée à des noms de variables uniques pour les faire entrer dans le champ d’application global comme indiqué ci-dessous. A ce stade, ils peuvent maintenant être appelés.

let f = closure()

let one = f[0]
let two = f[1]
let three = f[2]

one() // logs I was declared first
two() // logs I was declared second
three() // logs I was declared third

Pourquoi utiliser des closures ?

Vous vous demandez peut-être pourquoi se donner la peine de faire des closures. Eh bien, les closures ont un certain nombre d’utilisations et d’avantages.

Avant l’introduction des classes dans l’ES6, les closures permettaient de créer un moyen de créer une confidentialité de classe similaire à celle utilisée dans la programmation orientée objet, ce qui nous permettait d’émuler des méthodes privées. C’est ce qu’on appelle le module pattern et cela nous permet de construire un code maintenable avec une pollution réduite de l’espace de nommage et une plus grande réutilisabilité.

Examinons un cas où cela se produit.

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();

counter1.value(); // returns 0
counter1.increment(); // adds 1
counter1.increment(); // adds 1
counter1.value(); // returns 2
counter1.decrement(); //subtracts 1
counter1.value(); // returns 1
counter2.value(); // returns 0

Dans l’exemple ci-dessus, nous avons déclaré une fonction makeCounter qui est une fonction publique qui a accès à certaines variables privées comme privateCounter et les fonctions qui la manipulent. Cela imite le comportement de la création de makeCounter en tant que classe avec sa propre fonctionnalité et ses propres variables intégrées. On peut le voir quand on crée deux compteurs différents, counter1 et counter2. Chaque compteur est indépendant l’un de l’autre et fait référence à une version différente des variables.

Les closures nous permettent également d’utiliser des fonctions pour créer d’autres fonctions qui ajoutent une valeur spécifique à leur argument. Dans ce cas, la fonction parent permettant ce comportement est connue comme une function factory (fonction usine) car elle crée essentiellement d’autres fonctions.

En utilisant des fonctions usines, nous sommes capables d’obtenir un comportement connu sous le nom de Currying. que nous aborderons dans la section suivante.

Currying (ou Curryfication pour les pro-FR)

Le currying est l’ensemble des fonctions qui évaluent et renvoient immédiatement d’autres fonctions. Ceci est rendu possible par le fait que les fonctions Javascript sont des expressions qui peuvent renvoyer d’autres fonctions.

Les fonctions currying sont construites par enchaînement de closures en définissant et renvoyant immédiatement leurs fonctions internes simultanément.

Voici un exemple de currying :

let greeting = function (a) {
    return function (b) {
        return a + ' ' + b
    }
}

let hello = greeting('Hello')
let morning = greeting('Good morning')

hello('Austin') // returns Hello Austin
hello('Roy') // returns Hello Roy
morning('Austin') // returns Good morning Austin
morning('Roy') //returns Good Morning Roy

Les deux fonctions créées à partir de greeting(“hello” et “Good morning“) sont toutes deux des fonctions de retour qui traitent les entrées fournies pour générer un message d’accueil. Ils prennent également un argument qui est le nom de la personne à accueillir.

Dans le cas ci-dessus, le message d’accueil est également utilisé comme une fonction usine avec les deux fonctions hello et morning générées à partir de celui-ci.

La fonction interne peut également être appelée après le premier appel comme suit :

greeting('Hello There')('General Kenobi') 
//returns Hello There General Kenobi

Le currying est considéré comme faisant partie de la programmation fonctionnelle et, à ce titre, les fonctions de currying peuvent être facilement écrites à l’aide de la syntaxe de la fonction flèche dans ES6 et les versions plus récentes de Javascript pour un code plus propre et élégant.

let greeting = (a) => (b) => a + ' ' + b 

greeting('Hello There')('General Kenobi') 
//returns Hello There General Kenobi

Conclusion

Bien que les closures ne soient pas aussi couramment utilisées depuis que Javascript a incorporé des classes dans ES6, elles ont toujours leur place quand il s’agit d’écrire du code réutilisable propre. Les closures et le currying sont également des concepts importants à comprendre lorsqu’il s’agit de programmation fonctionnelle où ils servent essentiellement un but similaire aux méthodes privées dans la programmation orientée objet.

Dans la même veine vous pouvez aller voir mon article pour débutant sur les boucles en javascript


siddhy

Développeur web full stack depuis une 15aine d'année dans une agence web du sud de la France et Geek depuis toujours, l'apprentissage et le partage font parti intégrante de ma philosophie au même titre que l'évolution personnelle et la sagesse bouddhiste.

0 commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *