
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 ? Et 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 si 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 à ce 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 closure 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 environnement lexicaux différents. J'ai fait exprès de mettre les mêmes noms 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 sera 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
travaillera 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.