2010-10-18 23 views
5

mi pregunta es en realidad una de comprensión - Tengo una solución de trabajo, simplemente no entiendo cómo funciona.Preguntas de comprensión del Alcance Global Variable

Bien, entonces, lo que estoy tratando de hacer es agregar un setTimeout en un bucle, y pasar un valor cambiante a través de él. Ejemplo:

for (i=0;i<11;i++) 
{ 
    setTimeout("alert(i)",1000); 
} 

si he entendido correctamente, esto no funciona porque Javascript no lo hace (como PHP) pasar el valor de i a la función, pero pasa una referencia de i - que a su vez no es estática, sino que continúa para cambiar con el contador.

he encontrado una solución, que dice así:

for (i=0;i<11;i++) 
{ 
    setTimeout(function(x){return function(){alert(x)};}(i),1000); 
} 

yo no entiendo muy bien lo que esto hace realidad. Parece que pasa la función de "alerta" a la función de llamada, pero no puedo entenderlo.

Puedo trabajar con esta solución y también adaptarla a otros contextos, pero realmente me gustaría entender todo mi código, no solo usar cosas que encontré en algún lado y ser feliz de que funcione. Y, además, estoy buscando una versión más delgada para lograr el mismo objetivo.

Gracias, Marco

+3

+1 para "Realmente me gustaría entender todo mi código, no solo usar cosas que encontré en algún lado y ser feliz de que funcione" –

Respuesta

2

La razón por la que está llamando a una función que devuelve una función es que es necesario tener alguna forma para la función que se pasa al setTimeout() tener una referencia al valor actual de i.

Debido a que el código de espera para una duración de 1000 ms, el bucle for será completa antes de que funcione, y si el valor i será 11.

Pero debido a una función tiene su propio alcance variable, se puede pasar la valor de i en la función a la que se llama inmediatamente, de modo que se hace referencia a ella mediante la variable local x, que la función que se devuelve puede hacer referencia cuando finalmente la llama setTimeout().

for (i=0; i<11; i++) { 
    setTimeout(function(x){ 
       // CONTINUE HERE: 
       // x is a local variable to the function being executed 
       // which references the current value of i 

       // A function is being returned to the setTimeout that 
       // references the local x variable 
       return function(){ alert(x); }; 

       }(i) // START HERE: 
        // The "outer" function is executed immediately, passing the 
        // current value of "i" as the argument. 
    ,1000); 
} 

así que usted está terminando con un equivalente que sería algo como esto:

setTimeout(function(){ alert(x); }, 1000); //...where x === 0 
setTimeout(function(){ alert(x); }, 1000); //...where x === 1 
setTimeout(function(){ alert(x); }, 1000); //...where x === 2 
setTimeout(function(){ alert(x); }, 1000); //...where x === 3 
// etc. 
+0

Patrick, muchas gracias. Este fue probablemente el eslabón perdido. –

+0

@Marco P. - De nada. : o) – user113716

4

Lo que esto hace:

function(x){return function(){alert(x)};}(i) 

¿Se toma una función:

function(x){ ...code... } 

y lo ejecuta inmediatamente, pasando i (desde el bucle for) en como el único parámetro (para eso sirve el (i)). Este vuelve otra función:

function(){ alert(x); } 

Es que resultado que está siendo pasado a setTimeout() como la función TI llamadas cuando el temporizador, y no se hace referencia a la variable i en su bucle que está cambiando, es utilizando la copia que se pasó cuando creó la nueva función.

+0

Gracias por tomarse el tiempo para explicarme esto, y gracias también por el "+1". En realidad es cierto, toda mi página (airports.palzkill.de) - con la excepción de las cosas de Google - es selfmade. –

0

Patrick y Nick me ayudaron un gran problema en la comprensión de todo el asunto, por lo que me gustaría Resumirlo para todos los que tengan el mismo problema que yo:

setTimeout (así como algunas otras funciones con retardo de tiempo como eventlisteners) parece almacenar la devolución de llamada como una cadena, y luego utiliza algún tipo de evaluación interna en esta cadena, así lo interpreta como código.

Eso causa problemas con bucles y funciones con retardo de tiempo, ya que su referencia a la variable se refiere al resultado final de ese bucle, o tal vez a una variable que ni siquiera es global.

Según tengo entendido, la solución con la función-in-a-función resuelve este problema dando a la cadena de nuevo como un resultado de la función, que a su vez contiene el valor, no la referencia a una variable (alert("1") no alert(i)) .

Con respecto a hacer el código más corto, mi mente simple llegó a una solución simple. Como se espera que la devolución de llamada a ser una cadena, por qué no escribir el valor de variables en esta cadena, y luego devolvérsela:

for (i=0;i<11;i++) 
{ 
    setTimeout("alert("+i+")",1000); 
} 

Objetivamente esto no es probablemente la mejor solución, pero ya que requiere la menor cantidad de código y la cantidad más pequeña de recursos cerebrales para entender cómo y por qué funciona, opuesto a otras soluciones, puedo trabajar con esto por ahora.

¡Gracias de nuevo a Patrick, a Nick y al tipo desconocido que retiró su respuesta por tomarse el tiempo de ayudarme con esto!

+0

Marco - Lo tienes parcialmente bien. Un 'setTimeout' puede aceptar un String para su argumento, que luego intentará' eval'. Si existe un método (como alerta) en el espacio de nombres global, se disparará. Pero 'setTimeout' también puede aceptar un objeto de función como argumento. Esto se hace pasando una función anónima (que estaba volviendo el código en su ejemplo) o pasando una referencia de variable a una función. Si se recibe una función, se invocará en el espacio de nombres global independientemente del hecho de que pueda haberse originado en un ámbito privado. En js, puede pasar funciones de esta manera. – user113716