【问题标题】:Variable Global Scope understanding questions变量全局范围理解问题
【发布时间】:2010-10-18 15:46:08
【问题描述】:

我的问题实际上是一种理解——我有一个可行的解决方案,但我只是不明白它是如何工作的。

好的,所以 - 我要做的是在循环中添加一个 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);
}

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

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

谢谢,马可

【问题讨论】:

  • +1 表示“我真的很想了解我的所有代码,而不仅仅是使用我在某处找到的东西并且很高兴它可以工作”

标签: javascript variables scope


【解决方案1】:

这是做什么的:

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

是否需要一个函数:

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

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

function(){ alert(x); }

正是 result 被传递给 setTimeout() 作为函数 it 在计时器启动时调用,并且它没有在循环中引用变量 i变化,它使用的是创建新函数时传入的副本。

【讨论】:

  • 感谢 nick 花时间向我解释这一点 - 也感谢“+1”。事实上,我所有的页面(airports.palzkill.de)——除了谷歌的东西——都是自制的。
【解决方案2】:

您调用返回函数的函数的原因是您需要有某种方法让传递给setTimeout() 的函数引用@987654322 的当前 值@。

因为代码等待运行1000ms,所以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.

【讨论】:

  • 帕特里克,非常感谢。这可能是缺少的链接。
【解决方案3】:

Patrick 和 Nick 在理解整个事情方面帮助了我很多,所以我想为和我有同样问题的每个人总结一下:

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

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

据我了解,函数中函数的解决方案通过将字符串作为函数结果返回来解决此问题,该结果包含值,而不是对变量的引用(alert("1") 不是 @ 987654322@).

关于缩短代码,我简单的想法想到了一个简单的解决方案。由于回调应该是一个字符串,为什么不将变量值写入这个字符串,然后将其返回:

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

客观地说,这可能不是最好的解决方案,但由于它需要最少的代码和最少的大脑资源来理解它与其他解决方案相比如何以及为什么工作,我现在可以使用它。

再次感谢 Patrick、Nick 和不知名的人抽出时间帮助我解决这个问题!

【讨论】:

  • Marco - 你说对了一部分。 setTimeout 可以接受一个字符串作为其参数,然后它将尝试eval。如果全局命名空间中存在方法(如警报),它将触发。但是setTimeout 也可以接受一个函数对象作为参数。这是通过传递一个匿名函数(您的示例中的代码正在返回)或通过将变量引用传递给函数来完成的。如果接收到一个函数,它将在全局命名空间中调用,而不管它可能起源于私有范围。在js中,你可以像这样传递函数。
猜你喜欢
  • 2020-02-29
  • 2014-09-17
  • 1970-01-01
  • 2010-11-26
  • 2012-04-29
  • 2020-11-15
  • 2017-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多