2010-02-03 11 views
12

Los cierres en un bucle me están causando problemas. Creo que tengo que hacer otra función que devuelva una función para resolver el problema, pero no puedo hacer que funcione con mi código jQuery.Cierres en un bucle for

Aquí es el problema básico en una forma simplificada:

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

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

Naturalmente clic en cualquiera de los tres botones le dará una alerta diciendo 3. La funcionalidad que quiero es que al hacer clic en el botón 1 dará una alerta diciendo 1, botón 2 dirá 2 etc.

¿Cómo puedo hacer eso?

Respuesta

10

Consulte el método bind.

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

A partir de los documentos:

El parámetro opcional es EVENTDATA no se usa comúnmente. Cuando se proporciona, este argumento nos permite pasar información adicional al controlador. Uno a mano uso de este parámetro es trabajar en torno problemas causados ​​por los cierres

+0

Gracias, funciona. – Rob

+0

¡Trabajó para mí también! – bychkov

+0

+1, resuelve mi problema también – anvd

3

Utilice la función each de jQuery - supongo que un bucle a través de elementos similares - por lo que añadir el clic de usar algo como:

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

No probado, pero siempre uso esta estructura amable siempre que sea posible.

1

O simplemente fabrique una nueva función, como usted describe. Se vería así:

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

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

Estoy bastante seguro de que la solución de Mehrdad no funciona. Cuando ve personas copiar en una variable temporal, por lo general, se guarda el valor de "this" que puede ser diferente dentro de un alcance interno para niños.

+0

Sí, su solución no funcionó, creo que la eliminó. – Rob

+0

Esta es la mejor respuesta que conozco. El código con bind está bien, pero ese parámetro de datos es realmente un hack feo. Este enfoque tiene la ventaja de que funcionará siempre que tenga este problema (creando cierres que se refieren a una variable de bucle), tanto si tiene jQuery como si no. –

+0

@Jason: aunque estoy de acuerdo en que esta es una gran respuesta, y ciertamente la respuesta que habría dado si jQuery no hubiera sido mencionado y etiquetado en la pregunta, tengo que estar en desacuerdo sobre que es un "hack feo" - ciertamente es más nítido. Además, un 'Function.bind' semi-similar está en la especificación de la quinta edición, por lo que podría decirse que la mejor respuesta para plain ol 'js hubiera sido el método de creación de prototipos equivalente que proporcionaría esa funcionalidad en las ediciones actuales de js. http://snipplr.com/view/13987/functionbind/ es solo un ejemplo que pude encontrar, sé que hay muchos. –

5

@La solución de Andy es la más bonita. Pero también puede usar el alcance de Javascript para ayudarlo a ahorrar el valor de su cierre.

Para ello, cree un nuevo ámbito en su cuerpo de ciclo ejecutando una función anónima.

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

Puesto que el cuerpo de bucle es un nuevo ámbito de aplicación en cada iteración, la variable de índice se duplica con el valor correcto en cada iteración.

+0

gracias que es útil saber – Rob

6

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

Algunos puntos importantes aquí:

  • JavaScript es función de ámbito. Si desea un alcance nuevo ('más profundo'), debe crear una función para mantenerlo.
  • Esta solución es específica de Javascript, funciona con o sin jQuery.
  • La solución funciona porque cada valor de i se copia en un nuevo ámbito de aplicación como k, y la función de regresar de funMaker se cierra alrededor de k (que no cambia en el bucle), no alrededor de i (que lo hace).
  • Tu código no funciona porque la función que pasas a click no 'posee' el i, se cierra sobre el i de su creador, y que i cambia en el ciclo.
  • El ejemplo podría haberse escrito con funMaker en línea, pero generalmente uso tales funciones de ayuda para aclarar las cosas.
  • El argumento de funMaker es k, pero eso no hace ninguna diferencia, podría haber sido i sin ningún problema, ya que existe en el alcance de la función funMaker.
  • Uno de la explicación más clara del modelo de evaluación del 'medio ambiente' se encuentra en "Estructura e Interpretación de los programas de ordenador, por Sussman & Abelson (http://mitpress.mit.edu/sicp/ texto completo en línea, no es una lectura fácil) - véase la sección 3.2. Como JavaScript es realmente Scheme con sintaxis C, esa explicación es correcta.

EDIT: Se corrigió algunos signos de puntuación.

+0

Lo mismo que la respuesta de darkporter, pero con una buena exposición. * "JavaScript es realmente Scheme con sintaxis C" * Cada vez que alguien entiende esto por primera vez, un ángel JS obtiene sus alas. –