2010-06-30 13 views
8

Estoy usando jQuery y tengo una cosa extraña que no entiendo. Tengo algo de código:Cosas extrañas en JavaScript "para"

for (i = 1; i <= some_number; i++) { 
    $("#some_button" + i).click(function() { 
     alert(i); 
    }); 
} 

"#some_button", como su nombre lo dice - son algunos botones. Cuando se hace clic en ellos, deben aparecer un cuadro con su número, ¿correcto? Pero ellos no. Si hay 4 botones, siempre aparecen "5" (los botones cuentan +1). ¿Por qué es así?

+0

Es un problema muy común que la mayoría de las respuestas indican. Además, todas las preguntas etiquetadas con 'javascript', 'closures,' y 'loops' apuntarán a este problema y a las soluciones exactas - [javascript + closures + loops] (http://stackoverflow.com/questions/tagged/javascript+closures+ loops), incluido este ahora :) – Anurag

+0

necesitamos una forma para que SO tenga una gran ventana emergente que diga "ESTO HA SIDO RESPUESTA ANTES", con enlaces a la pregunta correcta. He visto al menos 3 versiones de javascript y 2 de python que hacen esta pregunta. – Claudiu

+1

@Claudiu: Desafortunadamente es muy difícil para los recién llegados a JavaScript/Python buscar este tipo de problemas. –

Respuesta

18

Tiene que ver con el alcance de JavaScript. Puede moverse con facilidad mediante la introducción de otro ámbito mediante la adición de una función y tener esa función llama a sí mismo y pase en i:

for (var i = 1; i <= some_number; i++) { 
    (function(j) { 
    $("#some_button" + j).click(function() { 
     alert(j); 
    }); 
    })(i); 
} 

Esto crea un cierre - cuando la función interna tiene acceso a un ámbito que ya no existe cuando se llama a la función. Consulte this article en el MDC para obtener más información.

EDIT: RE: Funciones de auto llamada: una función de auto llamada es una función que se llama de forma anónima. No lo instancias ni lo asignas a una variable. Se toma la forma siguiente (tenga en cuenta los parens de apertura):

(function(args) { 
    // function body that might modify args 
})(args_to_pass_in); 

Relacionando esto con la pregunta, el cuerpo de la función anónima sería:

$("#some_button" + j).click(function() { 
    alert(j); 
}); 

Combinando estos juntos, obtenemos la respuesta en el primer bloque de código La función de auto-llamada anónima espera un argumento llamado j. Busca cualquier elemento con una identificación de some_button con el valor entero de j al final (por ejemplo, some_button1, some_button10). Cada vez que se hace clic en uno de estos elementos, se alerta el valor de j. La penúltima línea de la solución pasa en el valor i, que es el contador de bucles donde se llama a la función de auto-llamada anónima. Hecho de otra manera, podría tener este aspecto:

var innerFunction = function(j) { 
    $("#some_button" + j).click(function() { 
    alert(j); 
    }); 
}; 

for (var i = 1; i <= some_number; i++) { 
    innerFunction(i); 
} 
+0

estimado dios :( Podría usted por favor, bastante por favor, vuelve a escribir de una manera más fácil de leer Nadie que doesn Ya sé que la respuesta alguna vez comprenderá algo de la pieza que escribiste. ');});}) (i);}' ... –

+1

@SF: esto es más o menos lo que tienes que hacer ... Tal vez esto te ayudará: gires 'por (...) {LOGICA (i); } ', donde' LOGIC' es un código arbitrario que opera en la variable 'i', en' for (...) {(FUNC) (i)} ', creando inmediatamente una función y dándole' i'. 'FUNC' es simplemente' function (j) {LOGIC (j); } ' – Claudiu

+0

Sí, puede pensar en un cierre como una función que tiene una referencia a cada variable externa que utiliza, en lugar de una copia. Este error surgió porque fui utilizado por referencia dentro del cierre. Esta solución lo resuelve copiando el valor en j, pasándolo a una función como argumento. –

9

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

Las variables incluidas en un cierre comparten el mismo entorno individual, por lo que cuando se llame al click, el ciclo habrá 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); 
    }; 
} 

var i; 

for (i = 0; i < some_number; i++) { 
    $("#some_button" + i).click(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:

-1
(function (some_number) { 
    for (i = 1; i <= some_number; i++) { 
     $("#some_button" + i).click(function() { 
     alert(i); 
     }); 
    } 
    })(some_number); 

englobar la función exterior porque para la velocidad y el hecho de que voy a volver a activar siempre.

+3

¿Estás seguro de que estás envolviendo la parte correcta? – HoLyVieR

2

Debido a que en el momento de hacer clic en ellos, i == 5.

+0

je muy sucinta, pero esto sólo ayudará a alguien que ya sabe cuál es el problema – Claudiu

+0

Whoa, acaba de recibir un upvote 6 meses más tarde .. de todos modos lo que el PO hizo realmente necesita es estudiar el concepto de "cierres", a continuación, no más miserias. –

0

Esto es debido a cómo funcionan los cierres en JavaScript.Cada una de las 5 funciones que está creando básicamente está compartiendo la misma variable i. El valor de i dentro de su función no se evalúa cuando crea la función, pero cuando ocurre el evento click, cuando el valor de i es 5.

Existen varias técnicas para evitar esto (cuando esto comportamiento no es lo que quieres). Uno (si tiene una función simple, como se hace aquí) es utilizar el constructor de funciones en lugar de un literal de función:

$("#some_button" + i).click(new Function("alert("+i+")"); 
-1

Este es un código muy inteligente. Tan inteligente que es una pregunta en SO. :) Dejé de lado la cuestión por completo, embruteciendo el código, solo para tener la oportunidad de entenderlo (o hacer que un colega lo entienda) dentro de seis meses. Los cierres tienen su lugar, pero en este caso los evitaría en favor de un código más comprensible.

Probablemente, me atribuyo la misma función a todos los botones, que recibirían el botón del evento, la tira "some_button" de la ID, y alertar al resultado. No es tan bonito, pero les garantizo que todos en la oficina podrían seguirlo de un vistazo.

+0

¿Es mejor tener un código complejo para solucionar un problema o evitar el problema por completo? –

Cuestiones relacionadas