我有这个脚本:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
但3
两次都会收到提醒,而不是1
然后2
。
有没有办法传递i
,而不将函数写为字符串?
答案 0 :(得分:311)
您必须安排为每个超时功能提供“i”的独特副本。
function doSetTimeout(i) {
setTimeout(function() { alert(i); }, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
如果你不做这样的事情(并且这个想法有其他变化),那么每个计时器处理函数将共享相同的变量“i”。当循环结束时,“i”的值是多少?这是3!通过使用中介函数,可以生成变量值的副本。由于超时处理程序是在该副本的上下文中创建的,因此它具有自己的私有“i”。
编辑 - 随着时间的推移有一些评论,其中一些混淆显而易见,因为设置一些超时会导致处理程序同时全部启动。重要的是要了解设置计时器的过程 - 对setTimeout()
的调用 - 几乎不需要任何时间。也就是说,告诉系统“请在1000毫秒后调用此函数”将几乎立即返回,因为在计时器队列中安装超时请求的过程非常快。
因此,如果发出超时请求的继承,就像OP中的代码和我的答案中的情况一样,并且每个时间延迟值相同,则一次已经过了一段时间,所有计时器处理程序将一个接一个地快速连续调用。
如果您需要定期调用处理程序,则可以使用setInterval()
,其名称与setTimeout()
完全相同,但在请求的重复延迟后会多次触发金额,或者您可以通过迭代计数器建立超时并乘以时间值。也就是说,修改我的示例代码:
function doScaledTimeout(i) {
setTimeout(function() {
alert(i);
}, i * 5000);
}
(100
毫秒超时,效果不会很明显,所以我将数字提高到5000.)i
的值乘以基本延迟值,所以在循环中调用5次将导致5秒,10秒,15秒,20秒和25秒的延迟。
<强>更新强>
2018年,有一个更简单的选择。由于在范围中声明变量的新功能比函数更窄,原始代码如果被修改则会起作用:
for (let i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
与let
不同,var
声明本身会导致循环的每次迭代都有一个不同的i
。
答案 1 :(得分:153)
您可以使用立即调用的函数表达式(IIFE)在setTimeout
周围创建一个闭包:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
答案 2 :(得分:26)
这是因为!
解决方案通过使用自我执行的函数(匿名的一个或更好的IIFE)并且拥有 i 的副本来为每个迭代声明一个范围强烈>在其中,像这样:
for (var i = 1; i <= 2; i++) {
(function(){
var j = i;
setTimeout(function() { console.log(j) }, 100);
})();
}
清洁工将是
for (var i = 1; i <= 2; i++) {
(function(i){
setTimeout(function() { console.log(i) }, 100);
})(i);
}
在每次迭代中使用IIFE(自执行函数)为每个迭代创建了一个新范围 迭代,它给我们的超时函数回调机会 关闭每个迭代的新范围,一个具有变量的范围 具有正确的每次迭代值,供我们访问。
答案 3 :(得分:25)
setTimeout
的函数参数正在关闭循环变量。循环在第一次超时之前结束,并显示i
的当前值,即3
。
因为JavaScript变量只有函数作用域,所以解决方案是将循环变量传递给设置超时的函数。您可以声明并调用这样的函数:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
答案 4 :(得分:9)
您可以使用extra arguments to setTimeout将参数传递给回调函数。
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
注意:这不适用于IE9及以下浏览器。
答案 5 :(得分:7)
<强> ANSWER 强>
我用它来制作动画,将物品添加到购物车中 - 点击时,购物车图标会从产品“添加”按钮浮动到购物车区域:
function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log('ADDED ONE!');
}, 1000*i);
}
};
注意持续时间是单位时间n epocs。
因此,从点击时刻开始,动画开始epoc(每个动画片段)是每个一秒单位乘以项目数的乘积。
epoc :https://en.wikipedia.org/wiki/Epoch_(reference_date)
希望这有帮助!
答案 6 :(得分:3)
您可以使用bind
方法
for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
答案 7 :(得分:2)
嗯,另一个基于Cody答案的工作解决方案,但更为通用可以是这样的:
function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}
function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}
yourFunction(timeInSeconds, counter); // well here are the values of your choice.
答案 8 :(得分:0)
一旦我解决了这个问题,我就遇到了同样的问题。
假设我需要12个延迟,间隔为2秒
function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}
var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay
答案 9 :(得分:-14)
真正的解决方案就在这里,但您需要熟悉PHP编程语言。 你必须混合PHP和JAVASCRIPT订单才能实现你的目的。
注意这个:
<?php
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);
</script>";
}
?>
它完全符合您的要求,但要注意如何在它们之间进行通气 PHP变量和JAVASCRIPT。