JavaScript : Les Closures
#1
Qu'est ce qu'une closure ?
 
Une closure c'est un fragment de code qui est composé de quatre éléments. Je vais faire un exemple pour voir comment on met en place une closure, comment ça marche et ensuite plus loin dans le tuto, on verra ce que l'on peut en faire.
- Dans la portée globale, je déclare une variable reference. Je ne l'initialise pas. Elle me servira plus tard.
- Toujours dans la portée globale, je déclare maintenant une fonction que j'appelle fGlobale.
- Dans la portée locale de cette fonction, je déclare une variable valueque j'initialise à 1.
- Puis, je déclare une fonction fLocalequi va faire unconsole.logdevalue. Mes quatre éléments sont là :fGlobale,fLocale,valueetreference.
- Maintenant, je mémorise l'adresse de fLocaledansreference.
- Puis, j'invoque fGlobale.
- Puis, j'invoque fLocaleen utilisantreference.
Qu'est-ce qu'il faut remarquer, au niveau de ce code ? Il faut remarquer que fLocale a besoin de value pour pouvoir s'exécuter. Maintenant, je l'exécute.
 
			Qu'est-ce que je constate au niveau de l'exécution ? Eh bien, je constate que fLocale a été capable d'afficher la valeur de la variable value. Pourtant, cette variable n'est pas dans la portée de fLocale. Ca veut dire que cette variable est mémorisée quelque part ailleurs. Vous pouvez vous contenter de cette explication, si ça vous suffit, mais je vais quand même détailler.
Cette variable value, au moment où j'exécute fGlobale, se trouve dans le contexte d'exécution de fGlobale. Ca c'est normal. Par contre, on pourrait penser qu'à la fin de l'exécution de fGlobale le contexte d'exécution soit dépilé de la pile d'exécution, et que value ne soit plus visible. Alors, il se trouve que ce n'est pas le cas à cause de la variable reference qui pointe sur fLocale ce qui empêche la libération du contexte d'exécution.
C'est à cet endroit-là, dans le contexte d'exécution de fGlobale, que fLocale au moment de son exécution va chercher value.
Qu'est-ce qui peut faire partie d'une closure ? Toutes les variables locales et tous les arguments de la fonction englobante (* ici fGlobale) peuvent faire partie de la closure. Le this et le tableau arguments ne peuvent pas en faire partie.
Par contre, variables ou arguments ne seront dans la closure, que s'ils sont utilisés dans la fonction locale (* ici fLocale). On va regarder ça :
- Je vais maintenant ajouter une variable value2àfLocale.
- Je vais aussi ajouter un console.log(value2)de manière quevalue2fasse partie de l'environnement d'exécution defLocale.
- Je vais ajouter une instruction debuggerde manière à mettre un point d'arrêt software (* tester sur Chrome).
On constate que value et value2 font partie de la closure. On peut remarquer aussi que la closure s'appelle fGlobale.
 
			Si je supprime le console.log(value2) alors value2 ne fait plus partie de la closure. C'est logique, car dans ces conditions fLocale n'en a pas besoin pour son exécution.
 
			
#2
Comment encapsuler une variable
 
En fait, je vais vous montrer comment encapsuler une variable dans ce que j'appelle un module. Alors qu'est-ce que j'entends par module ici ? Il s'agit d'une technique à base de closures qui a été popularisée par Douglas Crockford, un programmeur très célèbre, et qu'on utilisait avant que les modules JavaScript avec des import/export ne soient disponibles. Mais ça s'utilise encore.
- Je déclare une fonction anonyme et dedans, je déclare la variable que je veux encapsuler.
- Ensuite, je fais directement le returnd'un objet qui contient deux méthodes : une méthodesetet une méthodeget.
- Dans la méthode set, je mets une contrainte sur la valeur de l'argumentthe_value. Ca c'est pour bien montrer l'intérêt de l'encapsulation.
- Dans le get, je fais juste unreturn.
- Maintenant, cette fonction anonyme, je vais l'auto invoquer et je vais l'affecter à une variable valuequi va recevoir l'objet qui sera retourné. Donc, je déclare cette variable avec unconst.
 
			Faites bien la différence entre le const value qui est de type object et qui se trouve dans la portée globale et le let value qui est un Number et qui lui est local à la fonction anonyme. Ces deux value ne sont pas les mêmes et ils sont dans des environnements lexicaux différents. J'ai fait exprès de mettre le même nom pour créer cette petite difficulté et pour pouvoir l'expliquer.
L'intérêt de cette technique, c'est que vous êtes obligé de passer par les méthodes set et get pour manipuler le value du let value. En fait, la variable est encapsulée. Vous voyez que j'ai mis une contrainte sur les valeurs qui peuvent être affectées à value. Et il n'est pas possible de passer outre cette contrainte. C'est tout l'intérêt de cette construction.
#3
Plusieurs instances d'une closure
 
- Je crée deux boites avec deux div. Je place une classeboxpour les propriétés communes à ces deuxdivet deuxidpour les propriétés spécifiques à chacune d'entre elles.
- Je les style un petit peu. Je les place en relativepour que les boites soient positionnées et que je puisse les décaler. Ensuite, je mets une taille, une marge en dessous et je centre le contenu. Puis je mets deux couleurs debackgrounddifférentes.
- Maintenant, je passe au JavaScript, je déclare une fonction animationqui prend deux paramètres : le premiereltId, c'est l'id qui concerne la boite, le secondvit, est un paramètre de vitesse.
- Je récupère un pointeur eltvers la boîte dans l'arbre. J'initialise une variableposà0. Dansposje vais accumuler le décalage de la boîte. Puis j'appellesetInterval, et je lui passe comme callback une fonction fléchée.
- Dans cette fonction fléchée, je fais des pos += vitpour augmenter le décalage de ma boîte. Et je m'arrête lorsque je dépasse100pixels.
- Ensuite, j'appelle deux fois ma fonction animation. Un appel pour chaque boîte.
Je regarde ce que ce script donne à l'exécution puis je vais détailler le code.
 
			- Il y a deux appels à la fonction animationde manière à avoir deux instances de la closure.
- Pour chaque appel, il y a un couple indépendant de variables eltetposqui est alloué en pile d'exécution dans chaque closure. C'est ce que je veux dire par plusieurs instances d'une closure.
- Chaque callback passé en argument à l'appel à setIntervaltravaille sur son couple de variables.
- Notez également que c'est le console.log("C'est parti")qui s'exécute en premier. Les appels aux callbacks sont asynchrones et démarrent 100ms plus tard alors que le script semble terminé.
- Je donne deux valeurs différentes à vitau moment des deux appels àanimation. Ceci permet de prouver qu'il y a deux valeurs deposbien indépendantes si toutefois vous aviez encore un doute.

 
				 
				



