【问题标题】:setTimeout appears to be changing my variables! Why?setTimeout 似乎正在改变我的变量!为什么?
【发布时间】:2012-03-02 20:56:26
【问题描述】:

我会很快,直接跳到案子。代码已注释,因此您知道我的意图。基本上,我正在构建一个基于 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!
        }
}

【问题讨论】:

  • 不错的评论,请大家点赞。
  • 仅供参考,您的代码由于多种原因没有通过 jslint,并且会在某些浏览器中产生错误
  • @MarkSchultheiss,请解释一下,我认为 jsLint 是某种评估器,对吗?那么,你能告诉我为什么不计算吗?
  • 可能最简单的方法是查看:jsfiddle.net/h9tNJ - 单击 jslint 按钮。 - 注意额外的逗号肯定会在 IE 中导致错误。
  • 哦,我明白了。这也是一个非常有用的工具!我通常会把所有的东西都放进去,我会小心的,我想这只是一个例外......

标签: javascript html variables settimeout


【解决方案1】:

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

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

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

【讨论】:

  • +1,但要完全清楚:(function(iwas) { setTimeout(function() { console.log(iwas) }, 1000); })(i); -- 传入 'i'
【解决方案2】:

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

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

现在请记住闭包的东西,我们在setTimeout 中的匿名函数在它最终触发时(1000 毫秒后)仍然持有对i 的引用。所以它正确地显示了 2 的值。

如果您想在 1000 毫秒后显示每次迭代的数字,您需要调用另一个上下文。这可能看起来类似于

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 我也看到了隐含的全局变量 i
      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-14
        • 1970-01-01
        • 2023-03-11
        • 1970-01-01
        • 2017-11-20
        • 1970-01-01
        • 2013-05-28
        • 1970-01-01
        相关资源
        最近更新 更多