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
value
que j'initialise à 1. - Puis, je déclare une fonction
fLocale
qui va faire unconsole.log
devalue
. Mes quatre éléments sont là :fGlobale
,fLocale
,value
etreference
. - Maintenant, je mémorise l'adresse de
fLocale
dansreference
. - Puis, j'invoque
fGlobale
. - Puis, j'invoque
fLocale
en 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 quevalue2
fasse partie de l'environnement d'exécution defLocale
. - Je vais ajouter une instruction
debugger
de 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
return
d'un objet qui contient deux méthodes : une méthodeset
et 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
value
qui 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 classebox
pour les propriétés communes à ces deuxdiv
et deuxid
pour les propriétés spécifiques à chacune d'entre elles. - Je les style un petit peu. Je les place en
relative
pour 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 debackground
différentes. - Maintenant, je passe au JavaScript, je déclare une fonction
animation
qui 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
elt
vers la boîte dans l'arbre. J'initialise une variablepos
à0
. Danspos
je 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 += vit
pour augmenter le décalage de ma boîte. Et je m'arrête lorsque je dépasse100
pixels. - 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
animation
de manière à avoir deux instances de la closure. - Pour chaque appel, il y a un couple indépendant de variables
elt
etpos
qui 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 à
setInterval
travaille 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 à
vit
au moment des deux appels àanimation
. Ceci permet de prouver qu'il y a deux valeurs depos
bien indépendantes si toutefois vous aviez encore un doute.