我以为我用Javascript很好地理解了闭包,但显然我不是。以下代码不起作用。如何在延迟1秒后打印出控制台中0到9的所有数字?目前它只是打印" undefined"十次。
注意:我没有寻找更简单的锻炼来延迟打印数字。这个问题是关于理解闭包。
<script>
for(var i=0;i<10;i++){
setTimeout(function(i){console.log(i)}, 1000)
}
</script>
答案 0 :(得分: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;
的值。您也可以根据需要致电iInner
i
,但为了清晰起见,我使用了两个不同的名称。
还有一些助手可以使用,比如.bind
基本上会自动为你创建一个新的匿名函数,并传入这样的参数。
function(){
var i;
for(i=0;i<10;i++){
setTimeout(function(iInner){
console.log(iInner);
}.bind(null, i), 1000);
}
}
<func>.bind
将获取i
的值并返回一个新函数,在调用时将这些args传递给<func>
,并且您不必创建另一层嵌套。
答案 1 :(得分:0)
你已经定义了回调函数来获取参数i
,它隐藏了for循环中声明的可关闭i
。所以改成它:
<script>
for(var i=0;i<10;i++){
setTimeout(function(){console.log(i)}, 1000)
}
</script>
编辑:抱歉,我没有看到你问题的一般意图。就闭包的工作方式而言,函数对象捕获对从函数体内部引用的封闭范围中的任何变量的引用,该引用不绑定到任何局部变量(参数或显式var
声明)在体内。这就是你原来的尝试不起作用的原因; i
绑定到本地参数。
函数对象不捕获闭包变量在定义函数时拥有的值;它会捕获引用。因此,您不能迭代单个变量(我在谈论for循环中声明的var i
),意图是为每个后续函数定义关闭值。价值观没有关闭;变量是。
但是,可以通过在定义函数时具有要捕获的值的临时本地上闭包来有效地关闭值。它需要创建一个新的函数作用域并在该作用域内定义闭包函数,关闭具有所需值的局部(函数参数):
<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>
答案 2 :(得分:0)
这是你如何做到的。您创建一个IIFE(立即调用的函数表达式),它返回一个打印到控制台的函数。记住i的值,应该被内部函数吞噬,将它作为参数传递。
setTimeout期望“函数”作为第一个参数,这就是我们从IIFE返回函数的原因。
for(var i=0;i<10;i++){
setTimeout((function(i) {
return function() {
console.log(i);
}
})(i), 1000)
}
由于