2012-03-02 86 views
6

我會很快,並直接跳到案件。代碼被評論,所以你知道我的意圖。基本上,我正在構建一個基於HTML5的小型遊戲,並且反對在服務器或cookie中保存內容,我只是給玩家提供一個關卡代碼。當玩家將代碼(以簡單散列形式)輸入到文本輸入字段中,並單擊按鈕加載該級別時,將調用函數「l」。該函數首先檢索玩家的條目,然後迭代散列列表並對其進行比較。當一場比賽很喜歡,那個水平應該被加載,但是有錯誤。我做了一些調試,發現iterator(「i」)的值在setTimeout內部發生了變化!我想暫停1秒,因爲立即加載關卡會太快而且看起來很糟糕。setTimeout似乎正在改變我的變量!爲什麼?

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

不錯的評論,有所有upvotes。 – ninjagecko 2012-03-02 21:13:21

+0

僅供參考,您的代碼不會通過jslint的幾個原因,並會在一些瀏覽器中產生錯誤 – 2012-03-02 21:32:31

+0

@MarkSchultheiss,請解釋一下,我認爲jsLint是某種評估者,是對的嗎?那麼,你能告訴我爲什麼不計算? – corazza 2012-03-02 21:47:41

回答

3

for循環不斷遞增i直到循環條件滿足,即使在for循環的代碼不執行,當setTimeout代碼執行它顯示的i當前值 - 這是2

4

ECMAscript使用lexical closures的技術,這是對所有父上下文對象/詞彙環境記錄(ES3/ES5)的內部存儲。

簡而言之,您使用的匿名函數setTimeout關閉了該變量,因此當該超時值「等待」時,循環會繼續。 setTimeout operatores asynronously當然那又意味着,在時間循環已完成值,如果i當然是2

現在還記得關於封閉的東西,我們在setTimeout匿名函數仍持有到i時參考它終於發生(1000毫秒後)。所以它正確地顯示了你的值2.

如果你想在1000ms後顯示每次迭代的數字,你需要調用另一個上下文。這可能看起來類似於

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

+1例如清除本地 – 2012-03-02 21:29:39

5

其他人已經寫出了你所得到的行爲的原因。現在的解決方案:改變setTimeout行:

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

這工作,因爲它抓住了可變i的當前值到另一封,而封內的變量不發生改變。

+0

+1,但要說清楚:(function(iwas){ setTimeout(function(){console.log(iwas)},1000);})(i); - 通過'我'在 – 2012-03-02 21:26:54

1

的時候setTimeout的回調得到執行可變將有2的值,因爲你不退出循環,保持計數,直到它等於levelCodes.legnth(也就是2)。 基本上你需要在調用setTimeout後加返回中斷。 此外,你沒有聲明變量,所以它將被綁定到全局命名空間,這是不好的,可能會導致非常晦澀的錯誤。例如,可以在其他函數中更改值i,因此setTimeout回調將會看到不同的值。您需要添加var i;函數開頭l

+0

我看到這暗示全球變量我也 – 2012-03-02 21:30:21

相關問題