2012-03-02 21 views
6

Będę szybki i wskoczę prosto do walizki. Kod jest komentowany, więc znasz moje intencje. Zasadniczo buduję małą grę opartą na HTML5 i przeciwstawiając się zapisywaniu rzeczy na serwerze lub ciasteczku, po prostu podam graczowi kod poziomu. Kiedy gracz wprowadza kod (w formie prostego skrótu) do pola wprowadzania tekstu i klika przycisk, aby załadować ten poziom, wywoływana jest funkcja "l". Ta funkcja najpierw pobiera wpisy gracza, następnie przechodzi przez listę skrótów i porównuje je. Gdy mecz jest lubiany, ten poziom powinien zostać załadowany, ale wystąpiły błędy. Zrobiłem trochę debugowania i odkryłem, że wartość iteratora ("i") zmieniła się wewnątrz setTimeout! Chcę przerwać 1 sekundę, ponieważ natychmiastowe załadowanie poziomu byłoby po prostu zbyt szybkie i wyglądałoby źle.setTimeout wydaje się zmieniać moje zmienne! Czemu?

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

Przyjemne komentowanie, wszystkie przeboje. – ninjagecko

+0

FYI, twój kod nie przekazuje jslint z kilku powodów i spowoduje błędy w niektórych przeglądarkach. –

+0

@MarkSchultheiss, proszę wyjaśnić, myślę, że jsLint jest jakimś ewaluatorem, czy to prawda? Więc możesz mi powiedzieć, dlaczego to nie jest obliczenie? – corazza

Odpowiedz

3

Pętla for stale zwiększa i dopóki warunek pętli jest spełniony, mimo że kod w pętli for nie wykonuje, gdy kod w setTimeout wykonuje go pokazuje wartość prąd o i - co to 2.

4

ECMAscript wykorzystuje technikę lexical closures, która jest niczym innym niż wewnętrzną pamięcią do wszystkich obiektów nadrzędnego obiektu/rekordów środowiska leksykalnego (ES3/ES5).

Krótko mówiąc, twoja anonimowa funkcja używana przez setTimeout zamyka się nad tą zmienną i, więc podczas gdy ten czas oczekiwania "czeka", twoja pętla będzie kontynuowana. setTimeout operatores asynronously oczywiście a to z kolei oznacza, że ​​w momencie pętla zakończy wartość, jeśli jest oczywiście i 2.

Teraz pamiętać o rzeczy zamknięcia, nasz anonimowy funkcja w setTimeout nadal posiada odniesienie do i kiedy w końcu wystrzeliwuje (po 1000ms). Więc poprawnie pokazuje wartość 2.

Jeśli chcesz wyświetlić liczby dla każdej iteracji po 1000 ms, musisz wywołać inny kontekst. Może to wyglądać podobnie do

+0

+1 na przykład z wyraźnym lokalnym –

5

Inni już napisali przyczynę zachowania, które otrzymujesz. Teraz rozwiązanie: zmienić linię do setTimeout:

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

To działa, ponieważ oddaje aktualną wartość zmiennej i do kolejnego zamknięcia, a zmienna w tym zamknięcia nie zmienia.

+0

+1, ale żeby było całkowicie jasne: (function (iwas) { setTimeout (function() {console.log (iwas)}, 1000);}) (i); - przekazanie "i" w –

1

Przez zwrotnego czas setTimeout dostaje do wykonania zmienny i będzie miał wartość 2, ponieważ nie zamknąć pętlę i i utrzymuje liczenie aż będzie równa levelCodes.legnth (który jest 2). Zasadniczo po wywołaniu setTimeout należy dodać return lub przerwać. Ponadto, nie deklarujesz zmiennej i, więc będzie ona związana z globalną przestrzenią nazw, która jest zła i może potencjalnie prowadzić do bardzo niejasnych błędów. Na przykład wartość i można zmienić w innych funkcjach, więc wywołanie zwrotne setTimeout zobaczy inną wartość. Musisz dodać var i; na początku funkcji l.

+0

Widziałem tę domyślną zmienną globalną również –

Powiązane problemy