Partage
  • Partager sur Facebook
  • Partager sur Twitter

Closure

Sujet résolu
    18 avril 2011 à 14:39:57

    Bonjour, voici mon code:
    <script type="text/javascript">
    	var img = document.getElementsByTagName("img");
    	for(var i = 0;i < 8;i++){
    		img[i].onclick = function(){
    			img[i].style.width = "15%";
    		};
    	}
    </script>
    

    J'ai des images alignées et je voudrais que lorsqu'on clique sur l'une d'elle, elle grossisse.
    Seulement j'utilise une boucle car j'ai 8 images.
    Si je met "for(var i = 0;i < 8;i++){" il ne se passe rien, si je met "for(var i = 0;i < 7;i++){" seule la dernière image va faire ce que je lui dis de faire.
    Je sais qu'il y a un problème de closure car mon prof de javascript nous a un peu expliqué ça mais c'est une notion que je ne gère pas du tout.
    Comment je peux régler mon problème?
    • Partager sur Facebook
    • Partager sur Twitter
      18 avril 2011 à 15:47:46

      Pour que tu comprennes mieux le problèmes des closure, essaie de réfléchir aux problèmes des portées des variables.

      Dans ton code:
      function(){
      	img[i].style.width = "15%";
      };
      

      i fait référence à une variable externe à cette fonction.
      A ton avis, au moment où cette fonction est appelée, quelle est la valeur de i?
      indices: Cette fonction est appelée lorsque l'utilisateur clique sur une image.
      Donc la boucle est finie depuis longtemps.
      Donc la valeur de i, au moment où la fonction est appelée (quelque soit l'image) est ... 9


      Le but est donc de "fixer" cette valeur pendant la boucle. C'est là qu'intervient les closures.
      Si maintenant tu écris:
      function toto(i){
      	return function tata(){
      		alert(i);
      	};
      }
      
      Il s'agit de 2 fonctions que j'ai nommée toto et tata (contrairement à ton code où tu utilises des fonctions anonyme, je les ai nommé afin de faciliter l'explication). Lorsqu'on exécute la toto elle ne fait que retourner une référence à la fonction tata.
      ainsi si on veut exécuter directement la fonction tata il faudrait faire:
      toto("bonjour")(); //affiche bonjour
      
      A noter la présence de 2 paires de parenthèses.
      Dans la fonction tata il y a aussi une variable i. Cette variable i est aussi une variable externe à la fonction tata (elle n'a pas été déclarée dans cette fonction) elle fait référence à la variable i de la fonction toto (elle est déclarée dans les paramètres de la fonction toto)

      Maintenant revenons à ton code:
      function toto(i){
      	return function(){
      		img[i].style.width = "15%";
      	};
      }
      for(var i = 0;i < 8;i++){
      	img[i].onclick = toto(i);
      }
      
      Dans ce code j'ai créer une fonction extérieur nommée toto qui retournera le code à exécuter lorsqu'on clique sur l'image.

      Dans la boucle on exécute la fonction toto (une copie de la variable i est créée), et toto retourne la fonction à exécuter lors de l'action sur l'image. Cette dernière fonction, lorsqu'elle utilise la variable i, utilise la variable i créée par toto et correspond donc bien à la valeur souhaitée.
      Remarque: ma fonction utilise la variable img qui est elle-aussi externe. Afin que cette variable soit définie il faut que toto et la déclaration de img soit dans le même scope (dans la même fonction).


      A noter que tu peux aussi très bien utiliser une fonction anonyme (j'ai renommée la variable i dans la fonction par j afin de bien marquer la différence mais tu peux laisser i, cela ne pose pas de problème):
      img[i].onclick = function(j){
      	return function(){
      		img[j].style.width = "15%";
      	};
      }(i);
      

      • Partager sur Facebook
      • Partager sur Twitter
        18 avril 2011 à 15:50:46

        Utilise plutôt "img.length" qu'un chiffre, surtout quand ça peut varier.
        Ensuite, le problème est dans la fonction "img[i]". Au moment du clic sur n'importe quelle image, le i aura la valeur du dernier "chiffre" de la boucle (soit, dans ton exemple, 7)

        Une closure permet d'avoir le bon "i" lors de l'appel à la fonction (ça fait une copie locale de la variable dans la fonction, en gros)

        Il y a un très bon tuto sur les closures sur le SdZ, je te laisse y jeter un oeil ;)

        EDIT: Grilled, mais flemme de modifier :)
        • Partager sur Facebook
        • Partager sur Twitter
          18 avril 2011 à 17:34:19

          Ok, merci pour l'explication, c'est à peu prêt ce que nous avait dit notre prof, je comprends l'idée mais j'ai toujours du mal sur l'application.
          Merci à vous en tout cas ;)
          • Partager sur Facebook
          • Partager sur Twitter

          Closure

          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
          • Editeur
          • Markdown