Explorer les solutions

Parent Previous Next



Explorer les solutions



Tout d'abord, qu'est-ce qu'une closure ? En Javascript, il s'agit d'une fonction ayant pour but de capter des données susceptibles de changer au cours du temps, de les enregistrer dans son espace fonctionnel et de les fournir en cas de besoin.


Reprenons notre deuxième exemple et voyons comment lui créer une closure pour la variable i. Voici le code d'origine :

var divs = document.getElementsByTagName('div'),

    divsLen = divs.length;

 

for (var i = 0 ; i < divsLen ; i++) {

 

    setTimeout(function() {

        divs[i].style.display = 'block';

    }, 200 * i);

 

}


Actuellement, le problème se situe dans le fait que la variable i change de valeur avant même que nous n'ayons eu le temps d'agir. Le seul moyen serait donc d'enregistrer cette valeur quelque part. Essayons :

var divs = document.getElementsByTagName('div'),

    divsLen = divs.length;

 

for (var i = 0 ; i < divsLen ; i++) {

 

    var currentI = i; // Déclarer une variable DANS une boucle n'est pas conseillé, ici c'est juste pour l'exemple

 

    setTimeout(function() {

        divs[currentI].style.display = 'block';

    }, 200 * i);

 

}


Ligne 10, nous utilisons la variable i, car la fonction setTimeout() s'exécute immédiatement, la variable i n'a donc pas le temps de changer de valeur.


Malheureusement, cela ne fonctionne pas, car nous en revenons toujours au même : la variable currentI est réécrite à chaque tour de boucle, car le Javascript ne crée pas d'espace fonctionnel spécifique pour une boucle. Toute variable déclarée au sein d'une boucle est déclarée dans l'espace fonctionnel parent à la boucle. Cela nous empêche donc de converser avec la valeur écrite dans notre variable, car la variable est réécrite à chaque itération de la boucle.


Cependant, il est possible de contourner cette réécriture.


Actuellement, notre variable currentI est déclarée dans l'espace global de notre code. Que se passerait-il si nous la déclarions à l'intérieur d'une IEF ? Eh bien, la variable serait déclarée dans l'espace de la fonction, rendant impossible sa réécriture depuis l'extérieur.


Oui, mais si l'accès à cette variable est impossible depuis l'extérieur, comment peut-on alors l'utiliser pour notre setTimeout() ?


La réponse est simple : en utilisant le setTimeout()dans la fonction contenant la variable ! Essayons :

var divs = document.getElementsByTagName('div'),

    divsLen = divs.length;

 

for (var i = 0 ; i < divsLen ; i++) {

 

    (function() {

 

        var currentI = i;

 

        setTimeout(function() {

            divs[currentI].style.display = 'block';

        }, 200 * i);

 

    })();

 

}


Pratique, non ? Le fonctionnement peut paraître un peu absurde la première fois que l'on découvre ce concept, mais au final il est parfaitement logique.


Étudions le principe actuel de notre code : à chaque tour de boucle, une IEF est créée. À l'intérieur de cette dernière, une variable currentI est déclarée, puis nous lançons l'exécution différée d'une fonction anonyme faisant appel à cette même variable. Cette dernière fonction va utiliser la première (et la seule) variable currentI qu'elle connaît, celle déclarée dans notre IEF, car elle n'a pas accès aux autres variables currentI déclarées dans d'autres IEF.


Vous n'avez toujours pas oublié la première sous-partie de ce chapitre, n'est-ce pas ? Car, si nous avons traité le sujet des variables, c'est pour vous éviter une mauvaise compréhension à ce stade du chapitre. Ici nous avons un cas parfait de ce que nous avons étudié : currentI est déclarée dans une IEF, sa référence est donc détruite à la fin de l'exécution de l'IEF. Cependant, nous y avons toujours accès dans notre fonction anonyme exécutée en différé, car nous possédons une référence vers cette variable, ce qui évite sa suppression.


Dernière chose, vous risquerez de tomber assez fréquemment sur des closures plutôt écrites de cette manière :

var divs = document.getElementsByTagName('div'),

    divsLen = divs.length;

 

for (var i = 0 ; i < divsLen ; i++) {

 

    (function(currentI) {

 

        setTimeout(function() {

            divs[currentI].style.display = 'block';

        }, 200 * i);

 

    })(i);

 

}


Concrètement, qu'est-ce que l'on a fait ? Eh bien, nous avons tout simplement créé un argument currentI pour notre IEF et nous lui passons en paramètre la valeur de i. Cette modification fait gagner un peu d'espace (suppression de la ligne 8) et permet de mieux organiser le code, on distingue plus facilement ce qui constitue la closure ou non.


Tant que nous y sommes, nous pouvons nous permettre d'apporter une modification de plus à la ligne 6 :

var divs = document.getElementsByTagName('div'),

    divsLen = divs.length;

 

for (var i = 0 ; i < divsLen ; i++) {

 

    (function(i) {

 

        setTimeout(function() {

            divs[i].style.display = 'block';

        }, 200 * i);

 

    })(i);

 

}


Ainsi, même dans la closure, nous utilisons une variable nommée i. Cela est bien plus pratique à gérer et prête moins à confusion pour peu que l'on ait compris que dans la closure nous utilisons une variable idifférente de celle située en-dehors de la closure.


Voilà, vous savez maintenant vous servir des closures dans leur cadre général. Bien qu'elles existent sous plusieurs formes et pour plusieurs cas d'utilisation, nous avons ici étudié le cas principal.


Créé avec HelpNDoc Personal Edition: Produire facilement des livres électroniques Kindle

Site à deux balles