Fechamentos em um loop for

Fechamentos em um loop estão me causando problemas. Eu acho que tenho que fazer outra function que retorna uma function para resolver o problema, mas não consigo fazê-lo funcionar com o meu código jQuery.

Aqui está o problema básico de uma forma simplificada:

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

Clicando naturalmente em qualquer um dos três botões, aparecerá um alerta dizendo 3. A funcionalidade que eu quero é que clicando no botão 1, haverá um alerta dizendo 1, o botão 2 dirá 2 etc.

Como posso fazer isso?

Veja o método bind .

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

Dos docs:

O parâmetro eventData opcional não é comumente usado. Quando fornecido, esse argumento nos permite passar informações adicionais para o manipulador. Um uso prático desse parâmetro é solucionar problemas causados ​​por encerramentos

Experimente este código:

 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)); } 

Alguns pontos importantes aqui:

  • JavaScript tem escopo de function. Se você quiser um novo escopo (mais profundo), você precisa criar uma function para mantê-lo.
  • Esta solução é específica para Javascript, funciona com ou sem jQuery.
  • A solução funciona porque cada valor de i é copiado em um novo escopo como k , e a function retornada do funMaker fechada em torno de k (que não muda no loop), não em torno de i (o que faz).
  • Seu código não funciona porque a function que você passa para click não 'possui' o i , ele se fecha sobre o i do seu criador, e que i mudo no loop.
  • O exemplo poderia ter sido escrito com o funMaker , mas eu geralmente uso essas funções auxiliares para tornar as coisas mais claras.
  • O argumento do funMaker é k , mas isso não faz diferença, poderia ter sido i sem problemas, já que existe no escopo da function funMaker .
  • Uma das explicações mais claras do modelo de avaliação do “Ambiente” é encontrada em “Estrutura e Interpretação de Programas de Computador”, por Sussman & Abelson ( http://mitpress.mit.edu/sicp/ texto completo disponível on-line, não é uma leitura fácil ) - ver secção 3.2. Como o JavaScript é realmente Scheme com a syntax C, essa explicação está correta.

EDIT: Corrigido alguns pontuação.

A solução @Andy é a mais bonita. Mas você também pode usar o escopo de Javascript para ajudá-lo a salvar o valor em seu fechamento.

Você faz isso criando um novo escopo no corpo do loop executando uma function anônima.

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

Como o corpo do loop é um novo escopo em cada iteração, a variável de índice é duplicada com o valor correto em cada iteração.

Use a function .each do jquery – eu acho que você faz um loop através de elementos similares – então adicione o clique usando algo como:

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

Não testei, mas sempre uso essa estrutura amável quando possível.

Ou apenas fabricar uma nova function, como você descreve. Ficaria assim:

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

Tenho certeza que a solução de Mehrdad não funciona. Quando você vê pessoas copiando para uma variável temporária, geralmente é para salvar o valor de "this", que pode ser diferente dentro de um escopo filho interno.