2012-03-02 21 views
6

Voy a ser rápido y saltar directamente a la caja. El código está comentado para que conozcas mis intenciones. Básicamente, estoy construyendo un pequeño juego basado en HTML5, y en contra de guardar cosas en el servidor o en una cookie, solo le proporcionaré al jugador un código de nivel. Cuando un jugador ingresa el código (en forma de un hash simple) en un campo de entrada de texto, y hace clic en un botón para cargar ese nivel, se llama a la función "l". Esa función primero recupera las entradas del jugador, luego recorre la lista de hashes y los compara. Cuando una coincidencia es útil, se supone que ese cierto nivel debe estar cargado, pero hubo errores. Hice un poco de depuración y descubrí que el valor del iterador ("i") ha cambiado dentro de un setTimeout. Quiero hacer una pausa de 1 segundo, porque cargar el nivel inmediatamente sería demasiado rápido y se vería mal.¡setTimeout parece estar cambiando mis variables! ¿Por qué?

levelCodes = //Just a set of "hashes" that the player can enter to load a certain level. For now, only "code" matters. 
[ 
    {"code": "#tc454", "l": 0}, 
    {"code": "#tc723", "l": 1}, 
] 

var l = function() //This function is called when a button is pressed on the page 
{ 
    var toLoad = document.getElementById("lc").value; //This can be "#tc723", for example 

    for (i = 0; i < levelCodes.length; i++) //levelCodes.length == 2, so this should run 2 times, and in the last time i should be 1 
     if (levelCodes[i].code == toLoad) //If I put "#tc723" this will be true when i == 1, and this happens 
     { 
      console.log(i); //This says 1 
      setTimeout(function(){console.log(i)}, 1000); //This one says 2! 
     } 
} 
+1

Agradable comentario, tener todos los votos por votos. – ninjagecko

+0

FYI, su código no pasa jslint por varias razones y producirá errores en algunos navegadores –

+0

@MarkSchultheiss, por favor explique, creo que jsLint es una especie de evaluador, ¿es correcto? Bueno, entonces, ¿puedes decirme por qué no calcula esto? – corazza

Respuesta

3

El bucle for incrementos continuamente i hasta que se cumpla la condición de bucle, a pesar de que el código en el bucle no se ejecuta, cuando el código en el setTimeout lo ejecuta muestra el valor actual de i - que es 2.

4

ECMAscript usa la técnica de lexical closures que no es más que un almacenamiento interno para todos los objetos contextuales principales/registros de entorno léxico (ES3/ES5).

En pocas palabras, su función anónima utilizada por setTimeout cierra esa variable i, por lo que mientras ese tiempo de espera es "esperar", su ciclo continúa. setTimeout operatores asynronously de curso y eso significa que, a su vez, en el momento de ese bucle ha finalizado el valor si i es, por supuesto 2.

Ahora debe recordar acerca de las cosas de cierre, nuestra función anónima en setTimeout todavía mantiene una referencia a cuando i finalmente se dispara (después de 1000 ms). Por lo tanto, muestra correctamente el valor de 2.

Si desea mostrar los números de cada iteración después de 1000ms, debe invocar otro contexto. Esto podría parecerse a

setTimeout((function(local) { 
    return function() { 
     console.log(local); 
    }; 
}(i)), 1000); 
+0

+1 por ejemplo con –

5

Los demás ya indicaron el motivo del comportamiento que está recibiendo. Ahora la solución: cambiar la línea a setTimeout:

(function(i) { 
    setTimeout(function(){console.log(i)}, 1000); 
})(i); 

Esto funciona porque captura el valor actual de la variable i en otra de cierre, y la variable dentro de ese cierre no cambia.

+0

+1, pero para que quede totalmente claro: (function (iwas) { setTimeout (function() {console.log (iwas)}, 1000);}) (i); - pasando la 'i' en –

1

Por la devolución de llamada setTimeout tiempo llega a ejecutar la variable i a tener un valor de 2, ya que no sale del bucle y i mantiene contando hasta que sea igual a levelCodes.legnth (que es 2). Básicamente debe agregar return o break después de llamar a setTimeout. Además, no está declarando la variable i, por lo que estará vinculada al espacio de nombres global, lo cual es malo y puede provocar errores muy poco claros. Por ejemplo, el valor de i se puede cambiar en otras funciones, por lo que la devolución de llamada setTimeout verá un valor diferente. Necesita agregar var i; al principio de la función l.

+0

Vi esa variable global implícita i también –

Cuestiones relacionadas