请考虑以下代码:
<!DOCTYPE html>
<html>
<head>
<script>
function timedText()
{
var x=document.getElementById('txt');
var t = new Array();
t[1] = setTimeout( function(){x.value="2 seconds"}, 2000 );
t[2] = setTimeout( function(){x.value="4 seconds"}, 4000 );
t[3] = setTimeout( function(){x.value="6 seconds"}, 6000 );
}
function timedTextArr()
{
var x=document.getElementById('txt');
var t = new Array();
for( var i = 0 ; i < 3 ; i++ ) {
t[i] = setTimeout( function(){x.value=i*2+" seconds"}, i*2000 );
}
}
</script>
</head>
<body>
<form>
<input type="text" id="txt" />
<input type="button" value="Display timed text!" onclick="timedText()" />
<input type="button" value="Display timed text Arr!" onclick="timedTextArr()" />
</form>
<p>Click on the button above. The input field will tell you when two, four, and six seconds have passed.</p>
</body>
</html>
函数timedText()
有效,但timedTextArr()
没有。两个函数都将返回值从setTimeout()
分配给数组元素。但是在for()
循环中,只有最后一个计时器工作......并且它可以工作三次。
这是一个错误吗?
答案 0 :(得分:5)
这不是一个bug,看看Javascript中的闭包。
基本上你的for循环函数
function(){x.value=i*2+" seconds"}
只“看到”i变量的一个实例。
因此,一旦循环结束,i等于3,所有函数都为3。
您需要将调用包装在另一个匿名函数中以创建范围,如下所示:
t[i] = setTimeout( (function(i){ return function(){x.value=i*2+" seconds"}})(i), i*2000 );
外部函数将创建一个新范围,在其中我将等于循环中i的值并保持这样。你可以在那里试试:http://jsfiddle.net/6b68E/
答案 1 :(得分:5)
您的函数中的i
引用循环中的i
,在任何超时发生时为6
。您需要添加一个闭包/范围:
for( var i = 0 ; i < 3 ; i++ ) {
(function(){ // create a closure (new scope)
var _i = i; // make a local copy of `i` from the outer scope
t[i] = setTimeout( function(){x.value=_i*2+" seconds"}, i*2000 );
})();
}
答案 2 :(得分:1)
主要问题是由于计时器功能的异步性质,循环在第一次超时被调用之前就已经完成,因此在第一次1>时将i
设置为2 em>超时运行,并且对于其他两个超时保持不变
要解决此问题,您应该考虑重构代码以使用 interval ,这样您就可以与闭包同步更改i
的值:
var i=1;
var handle = setInterval( function() {
x.value = (i*2) + "seconds";
i++;
if (i>3) clearInterval(handle);
}, 2000 );
除此之外,循环从0运行到2,而不是1到3,如timedText()
答案 3 :(得分:1)
您获得相同结果的原因是setTimeout
是异步的。这意味着它在脚本的其余部分完成后才会运行。然后一旦运行,i
的值设置为等于3,所以所有函数都运行,他们看到的只是i = 3.