变量全局范围理解问题

时间:2010-10-18 15:46:09

标签: javascript variables scope

我的问题实际上是理解之一 - 我有一个有效的解决方案,我只是不明白它是如何工作的。

好的,所以 - 我要做的是在循环中添加一个setTimeout,并通过它传递一个更改的值。例如:

for (i=0;i<11;i++)
{
     setTimeout("alert(i)",1000);
}

如果我理解正确,这不起作用,因为Javascript不会(像PHP)将i的值传递给函数,但是传递i的引用 - 这反过来不是静态的,而是继续随计数器更改。

我找到了一个解决方案,如下所示:

for (i=0;i<11;i++)
{
    setTimeout(function(x){return function(){alert(x)};}(i),1000);
}

我真的不明白这实际上是做什么的。看起来它将“警报”功能传递回调用函数,但我无法理解这一点。

我可以使用这个解决方案并将其适应其他环境,但我真的很想了解我的所有代码,而不仅仅是使用我在某个地方找到的东西,并且很高兴它的工作原理。此外,我正在寻找更轻薄的版本来实现同样的目标。

谢谢,Marco

3 个答案:

答案 0 :(得分:4)

这是做什么的:

function(x){return function(){alert(x)};}(i)

是否需要一个功能:

function(x){ ...code... }

并立即执行 ,将i(来自for循环)作为唯一参数传递(这就是最后(i)的结果)。这个返回另一个函数:

function(){ alert(x); }

结果正在传递给setTimeout(),因为函数在计时器启动时调用它并且它没有引用变量i在你正在改变的循环中,它正在使用创建新函数时传入的副本。

答案 1 :(得分:2)

您调用返回函数的函数的原因是您需要为传递给setTimeout()的函数提供一些方法来引用当前的值i

由于代码等待运行1000毫秒,for循环将在运行之前完成,i的值将为11。

但是因为函数有自己的变量作用域,所以可以将i的值传递给立即调用的函数,以便它由局部变量x引用,正在返回的函数可以在setTimeout()最终调用它时引用。

for (i=0; i<11; i++) {
    setTimeout(function(x){
                 // CONTINUE HERE:
                 // x is a local variable to the function being executed
                 //    which references the current value of i

                 // A function is being returned to the setTimeout that
                 //    references the local x variable
                 return function(){ alert(x); };

               }(i) // START HERE:
                    // The "outer" function is executed immediately, passing the
                    //   current value of "i" as the argument.
     ,1000);
}

所以你最终得到了类似的东西:

setTimeout( function(){ alert(x); }, 1000); //...where x === 0
setTimeout( function(){ alert(x); }, 1000); //...where x === 1
setTimeout( function(){ alert(x); }, 1000); //...where x === 2
setTimeout( function(){ alert(x); }, 1000); //...where x === 3
// etc.

答案 2 :(得分:0)

帕特里克和尼克帮助我理解了整个事情,所以我想总结一下,每个人都和我有同样的问题:

setTimeout(以及其他一些延时函数,如eventlisteners)似乎将回调存储为字符串,然后在此字符串上使用某种内部eval,从而将其解释为代码。

这导致循环和时间延迟函数出现问题,因为它们对变量的引用是指该循环的最终结果,或者可能是甚至不是全局变量。

据我了解,具有函数函数的解决方案通过将字符串作为函数结果返回来解决此问题,然后函数结果包含值,而不是对变量的引用(alert("1")不是alert(i))。

关于使代码更短,我的简单思想来到了一个简单的解决方案。由于回调预计是一个字符串,为什么不将变量值写入此字符串,然后将其返回:

for (i=0;i<11;i++)
{
     setTimeout("alert("+i+")",1000);
}

客观地说,这可能不是最好的解决方案,但是因为它需要最少量的代码和最少量的大脑资源才能理解它与其他解决方案相反的工作方式和原因,我现在可以使用它。

再次感谢帕特里克,尼克和那个不知名的人,他抽出了他的答案,花时间帮助我!