2010-05-10 10 views
14

Soy relativamente nuevo en JS, así que esto puede ser un problema común, pero noté algo extraño cuando se trata de bucles for y la función onclick. Yo era capaz de replicar el problema con este código:JavaScript for loop index raregeness

<html> 
<head> 

<script type="text/javascript"> 
window.onload = function() { 
    var buttons = document.getElementsByTagName('a'); 
    for (var i=0; i<2; i++) { 
     buttons[i].onclick = function() { 
      alert(i); 
      return false; 
     } 
    } 
} 

</script> 

</head> 

<body> 
<a href="">hi</a> 
<br /> 
<a href="">bye</a> 

</body> 

</html> 

Al hacer clic en los enlaces que cabe esperar para conseguir '0' y '1', pero en vez recibo el mensaje '2' para los dos. ¿Por qué es esto?

Por cierto, logré resolver mi problema particular usando la palabra clave 'this', pero aún tengo curiosidad sobre qué hay detrás de este comportamiento.

+0

Por favor, muestre un ejemplo de cómo resolvió el problema con "esto". –

Respuesta

14

Usted está teniendo a very common closure problem en el circuito for.

Las variables incluidas en un cierre comparten el mismo entorno, por lo que cuando se ejecuta la devolución de llamada onclick, el ciclo ha seguido su curso y la variable i se mantendrá hacia la última entrada.

Puede resolver esto con incluso más cierres, utilizando una fábrica de función:

function makeOnClickCallback(i) { 
    return function() { 
     alert(i); 
     return false; 
    }; 
} 

var i; 

for (i = 0; i < 2; i++) { 
    buttons[i].onclick = makeOnClickCallback(i); 
} 

Este puede ser un tema bastante complicado, si usted no está familiarizado con el funcionamiento de los cierres. Usted puede comprobar hacia fuera el siguiente artículo de Mozilla para una breve introducción:


Nota: Me gustaría también sugieren no utilizar var dentro del bucle for, porque esto puede engañar en la creencia de que la variable i tiene alcance de bloque, cuando por otro lado la variable i es como la variable buttons, dentro de la función.

0

Es una orden de ejecución tema

How to assign event callbacks iterating an array in javascript (jQuery)

Básicamente, el controlador de clic accesos i bien después del bucle ha salido, y por lo tanto i es igual a 2.

+0

También es un problema de alcance, 'i' está dando vueltas cuando no es evidente que debería. – Greg

+0

Correcto, por lo que me vinculé a una publicación mía sobre cierres. –

9

Debe almacenar el estado de la variable i, ya que cuando se desencadena el evento, el estado del alcance de i ha aumentado hasta el recuento máximo de bucles.

window.onload = function() { 
    var buttons = document.getElementsByTagName('a'); 
    for (var i=0; i<2; i++) { 
     (function (i) { 
      buttons[i].onclick = function() { 
       alert(i); 
       return false; 
      } 
     })(i); 
    } 
} 

El ejemplo anterior crea una función anónima con un solo argumento i, que luego se llamó con i que se pasa como ese argumento. Esto crea una nueva variable en un ámbito separado, guardando el valor tal como estaba en el momento de esa iteración particular.

+1

No se recomienda crear funciones en un ciclo –

+0

@Ryan: No estoy seguro de si eso es pedantería o confusión al hablar. Está repitiendo una advertencia JSLint/JSHint sin tomarse el tiempo para entender lo que significa. Eche un vistazo a los [documentos] (http: //www.jshint.com/docs/options/# loopfunc) para un ejemplo de una función que no debería crearse en un bucle. Los IIFE son aceptables porque es prácticamente la única opción en algunos escenarios hasta que 'let' sea universalmente compatible. La respuesta aceptada también crea funciones en bucles, sin voto negativo ni comentarios allí. –

+0

De acuerdo, déjame aclarar: estás creando * dos * funciones en un bucle. El IIFE y el manejador de clics. La respuesta aceptada es crear una función en un bucle: el manejador de clics. Por lo tanto, la respuesta aceptada minimiza las ineficiencias, mientras que la suya las maximiza. –