setTimeout似乎正在改变我的变量!为什么?

时间:2012-03-02 20:56:27

标签: javascript html variables settimeout

我会很快并直接跳到案件中。代码被评论,所以你知道我的意图。基本上,我正在构建一个基于HTML5的小型游戏,并且反对在服务器或cookie中保存内容,我只是向玩家提供一个级别代码。当玩家将代码(以简单散列的形式)输入文本输入字段,并单击按钮以加载该级别时,将调用函数“l”。该函数首先检索玩家的条目,然后遍历散列列表并进行比较。当匹配喜欢时,应该加载某个级别,但是存在错误。我做了一些调试,我发现迭代器(“i”)的值在setTimeout内发生了变化!我想暂停1秒,因为立即加载水平会太快而且看起来很糟糕。

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!
        }
}

5 个答案:

答案 0 :(得分:6)

其他人已经写出了你获得的行为的原因。现在解决方案:将setTimeout行更改为:

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

这是有效的,因为它将变量i的当前值捕获到另一个闭包中,并且该闭包内的变量不会改变。

答案 1 :(得分:4)

ECMAscript使用lexical closures的技术,它只是所有父上下文对象/词汇环境记录(ES3 / ES5)的内部存储。

简而言之,setTimeout使用的匿名函数会关闭i变量,因此当超时“等待”时,循环会继续。 setTimeout当然是异步操作,反过来意味着,当循环结束时,如果i当然是2。

现在记住关闭的东西,setTimeout中的匿名函数在最终触发时(1000ms后)仍然保留对i的引用。所以它正确地显示了2的值。

如果要在1000ms后显示每次迭代的数字,则需要调用另一个上下文。这可能类似于

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

答案 2 :(得分:3)

for循环连续递增i直到满足循环条件,即使for循环中的代码没有执行,当setTimeout中的代码执行时它显示当前 i的值 - 2

答案 3 :(得分:1)

当setTimeout回调变为执行变量时, i 的值为2,因为你没有退出循环并且 i 继续计数,直到它等于levelCodes.legnth(即2)。 基本上你需要在调用setTimeout后添加 return break 。 此外,您没有声明变量 i ,因此它将绑定到全局命名空间,这很糟糕,可能会导致非常模糊的错误。例如, i 的值可以在其他函数中更改,因此setTimeout回调将看到不同的值。您需要在函数 l 的开头添加 var i;

答案 4 :(得分:0)

您可以传递第三个参数,该参数将在超时时间后更改,这将是该函数的第一个参数。例如。

setTimeinterval(function(x){console.log(x)},1000,i)