2014-12-06 43 views
0

我以为我用Javascript理解了闭包,但显然我没有。以下代码不起作用。如何在延迟1秒后打印出控制台中从0到9的所有数字?目前它只打印“未定义”十次。带有Javascript回调的闭包

注意:我不想寻找一个简单的锻炼来延迟打印数字。这个问题是关于理解闭包。

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(i){console.log(i)}, 1000) 
} 
</script> 

回答

2

闭包是在JavaScript中很重要,但要了解你是closing正是在这一点很重要。重做你当前的代码(种类)。

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     setTimeout(function(){ 
      console.log(i); 
     }, 1000); 
    } 
} 

在这个例子中,你的代码基本上是关闭了var i;,这意味着当定时器运行时,它会读取该的var i;值并打印出来。在这种情况下,如您所见,定时器运行时,循环已完成,值为10

你想要做的是创建一个新的函数范围,在特定的时间捕获i的值。

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     (function(iInner){ 
      setTimeout(function(){ 
       console.log(iInner); 
      }, 1000); 
     })(i); 
    } 
} 

这个例子将创建一个新的匿名函数,然后在循环立即调用它,并传递的i当前值到它,所以,当你的计时器读取iInner,它会读取这可是价值传递给函数,而不是来自var i;的值。如果需要,您也可以拨打iInneri,但为了清晰起见,我使用了两个不同的名称。

还有一些助手可以使用,例如.bind,它们本质上会自动为您创建一个新的匿名函数,并传递像这样的参数。

function(){ 
    var i; 
    for(i=0;i<10;i++){ 
     setTimeout(function(iInner){ 
      console.log(iInner); 
     }.bind(null, i), 1000); 
    } 
} 

<func>.bind将采取i的值,并返回调用时通过对<func>那些ARGS一个新的功能,您就不必创建嵌套的另一层。

0

您已经定义了回调函数取参数i,这是掩盖在声明for循环closurable i。因此,将其更改为:

<script> 
for(var i=0;i<10;i++){ 
    setTimeout(function(){console.log(i)}, 1000) 
} 
</script> 

编辑:对不起,我不看你的问题的一般意图。就封闭方式而言,函数对象捕获对函数体内引用的封闭范围中的任何变量的引用,该引用不绑定到主体内的任何局部变量(参数或显式var声明) 。这就是为什么你的原始尝试不起作用; i绑定到本地参数。

功能对象不会捕获该合拢变量具有在被定义的功能的时间;它捕获了一个参考它。因此,您无法迭代单个变量(我正在讨论在for循环中声明的var i),意图是为每个后续函数定义关闭。价值观没有关闭;变量是。

但是,您可以通过关闭一个临时本地来有效地关闭一个值,该临时本地具有您要在定义该函数时捕获的值。它需要创建一个新的功能范围和界定范围内的closuring功能,围绕本地(函数参数)closuring有你想要的值:

<script> 
for (var i_outer = 0; i_outer < 10; ++i_outer) 
    setTimeout((function(i_inner) { return function() { console.log(i_inner); }; })(i_outer), 1000); 
</script> 
+0

不!这只是打印10次。这就是为什么我们需要关闭。 – 2014-12-06 20:46:36

0

这是你怎么做。您创建了一个IIFE(立即调用的函数表达式),该函数返回一个打印到控制台的函数。记住我的价值,应该被内在的功能所吞噬,通过将它作为一个参数来传递。

setTimeout需要一个“函数”作为第一个参数,这就是我们从IIFE返回函数的原因。

for(var i=0;i<10;i++){ 

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

感谢