2010-09-08 18 views
5

Ejecutar el siguiente código:alcance JavaScript con cierre: me ayuda a entender

for (var i=0; i<3; i++) { 
    setTimeout(function() { console.log(i); } , 500); 
} 

Salidas "3" tres veces. Está emitiendo el valor final de i en comparación con el valor de i cuando se crea la función interna.

Si quiero que la salida sea 1, 2 y 3, ¿cómo escribiría este código? ¿Cómo puedo hacer que use el valor de i en el momento en que se define la función en lugar de su valor final?

Respuesta

6
for (var i=0; i<3; i++) { 
    setTimeout(function(val) { return function() { console.log(val); } }(i), 500); 
} 

Así, en setTimeout tiempo (en el momento que definen la función de setTimeout), estamos llamando a la función anónima teniendo val como parámetro. Esto crea un cierre para cada llamada de función, almacenando el valor de val dentro del alcance de la función que acabamos de llamar. Usé un self-invoking function, que crea un closure inmediato.

En el código que proporcionó, el código crea un cierre, pero para el alcance más amplio de la totalidad del código, entonces i es local para todo el código, lo que significa que en tiempo de ejecución la función anónima usará variable i que utiliza el resto del código.

+0

Este ejemplo utiliza dos funciones anónimas, mientras que la respuesta de @ z5h utiliza un nombre función, que puede ilustrar el concepto más claramente. – palswim

4
function f(i){ 
    return function(){console.log(i);}; 
} 

for (var i=0; i<3; i++) { 
    setTimeout( 
    f(i) 
    , 500); 
} 
1

alternativa:

for (var i=0; i<3; i++) { 
    (function(val){ 
     setTimeout(function() { 
      console.log(val); 
     },500) 
    }(i)); 
} 
2

La alternativa moderna a un cierre explícito (que puede ser un poco difícil de leer cuando se tiene una función de doble envuelta) es Function#bind. Una vez que haya hacked in support para los navegadores que no lo hacen ECMAScript quinta edición, sin embargo, se puede decir:

for (var i=0; i<3; i++) { 
    setTimeout(function(i) { console.log(i); }.bind(window, i), 500); 
} 

la window es el valor que this habrá dentro de la función (que no es necesario un this aquí, entonces solo usamos el objeto global predeterminado). En el caso de que usted está llamando a otra función/método, como aquí con console.log, se puede usar eso para extirpar la expresión de la función por completo:

for (var i=0; i<3; i++) { 
    setTimeout(console.log.bind(console, i), 500); 
}