2010-02-03 7 views
12

Les fermetures dans une boucle me causent des problèmes. Je pense que je dois faire une autre fonction qui renvoie une fonction pour résoudre le problème, mais je ne peux pas l'obtenir pour fonctionner avec mon code jQuery.Fermetures dans une boucle for

Voici le problème fondamental sous une forme simplifiée:

function foo(val) { 
    alert(val); 
} 

for (var i = 0; i < 3; i++) { 
    $('#button'+i).click(function(){ 
    foo(i); 
    }); 
} 

cliquant naturellement sur l'un des trois boutons donnera une alerte en disant 3. La fonctionnalité que je veux est que cliquer sur le bouton 1 donnera une alerte disant 1, le bouton 2 dira 2 etc.

Comment puis-je le faire faire cela?

Répondre

10

Voir la méthode bind.

$('#button'+i).bind('click', {button: i}, function(event) { 
    foo(event.data.button); 
}); 

De la documentation:

Le paramètre optionnel EventData est pas couramment utilisé. Lorsqu'il est fourni, cet argument nous permet de transmettre des informations supplémentaires au gestionnaire. Une pratique utilisation de ce paramètre est de travailler autour de problèmes causés par les fermetures

+0

Merci, cela fonctionne. – Rob

+0

A travaillé pour moi aussi! – bychkov

+0

+1, résoudre mon problème aussi – anvd

3

Utilisez la fonction .each de jquery - Je suppose que vous une boucle à travers des éléments similaires - il faut donc ajouter le clic en utilisant quelque chose comme:

$(element).children(class).each(function(i){ 
    $(this).click(function(){ 
     foo(i); 
    }); 
}); 

Non testé mais j'utilise toujours cette structure si possible.

1

Ou juste de fabriquer une nouvelle fonction, comme vous le décrivez. Cela ressemblerait à ceci:

function foo(val) { 
    return function() { 
     alert(val); 
    } 
} 

for (var i = 0; i < 3; i++) { 
    $('#button'+i).click(foo(i)); 
} 

Je suis assez sûr que la solution de Mehrdad ne fonctionne pas. Lorsque vous voyez des personnes copier dans une variable temporaire, c'est généralement pour enregistrer la valeur de "this" qui peut être différente dans une portée enfant interne.

+0

Oui, sa solution n'a pas fonctionné, je pense qu'il l'a supprimé. – Rob

+0

C'est la meilleure réponse que je connaisse. Le code avec bind est bien, mais ce paramètre de données est vraiment un hack laid. Cette approche présente l'avantage de fonctionner à chaque fois que vous rencontrez ce problème (création de fermetures faisant référence à une variable de boucle), que vous ayez jQuery ou non. –

+0

@Jason: même si je suis d'accord c'est une bonne réponse, et certainement la réponse que j'aurais donnée si jQuery n'étaient pas mentionnés et marqués dans la question, je ne suis pas d'accord sur le fait qu'il s'agit d'un "vilain bidouille". En outre, un 'Function.bind 'semi-semblable est dans la spécification de la 5ème édition, donc vous pourriez dire que la meilleure réponse pour plaine ol' js aurait été la méthode de prototypage équivalente qui fournirait cette fonctionnalité dans les éditions actuelles de js. http://snipplr.com/view/13987/functionbind/ est juste un exemple que je pourrais trouver, je sais qu'il y en a beaucoup. –

5

La solution @Andy est la plus belle. Mais vous pouvez également utiliser le scoping Javascript pour vous aider à sauvegarder la valeur de votre fermeture.

Vous le faites en créant une nouvelle portée dans votre corps de boucle en exécutant une fonction anonyme.

for (var i = 0; i < 3; i++) { 
    (function(){ 
    var index = i; 
    $('#button'+index).click(function(){ 
     foo(index); 
    }); 
    })(); 
} 

Etant donné que le corps de la boucle est un nouveau champ d'application à chaque itération, la variable d'index est dupliquée avec la valeur correcte à chaque itération.

+0

merci cela est utile de savoir – Rob

6

Essayez ce code:

function foo(val) { 
    alert(val); 
} 

var funMaker = function(k) { 
    return function() { 
    foo(k); 
    }; 
}; 

for (var i = 0; i < 3; i++) { 
    $('#button'+i).click(funMaker(i)); 
} 

Quelques points importants ici:

  • JavaScript est fonction scope. Si vous voulez une nouvelle portée (plus profonde), vous devez créer une fonction pour la maintenir.
  • Cette solution est spécifique à Javascript, elle fonctionne avec ou sans jQuery.
  • La solution fonctionne parce que chaque valeur de i est copiée dans une nouvelle étendue en tant que k, et la fonction de retour de funMaker se ferme autour de k (qui ne change pas dans la boucle), et non pas autour de i (qui fait).
  • Votre code ne fonctionne pas car la fonction que vous transmettez à click ne possède pas le i, il se ferme sur le i de son créateur, et que i change dans la boucle.
  • L'exemple aurait pu être écrit avec funMaker en ligne, mais j'utilise généralement de telles fonctions auxiliaires pour rendre les choses plus claires.
  • L'argument de funMaker est k, mais cela ne fait aucune différence, il aurait pu être i sans aucun problème, car il existe dans le cadre de la fonction funMaker. L'une des explications les plus claires du modèle d'évaluation 'Environnement' se trouve dans 'Structure et Interprétation des Programmes Informatiques', par Sussman & Abelson (http://mitpress.mit.edu/sicp/ texte complet disponible en ligne, pas facile à lire) - voir section 3.2. Puisque JavaScript est vraiment Scheme avec la syntaxe C, cette explication est OK.

EDIT: Correction d'une ponctuation.

+0

La même chose que la réponse de darkporter, mais avec une belle exposition. * "JavaScript est vraiment Scheme avec la syntaxe C" * Chaque fois que quelqu'un comprend cela pour la première fois, un ange JS obtient ses ailes. –

Questions connexes