我有这个脚本:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
但是3
两次都收到警报,而不是1
then 2
。
有没有办法 pass i
,而不将函数写成字符串?
我有这个脚本:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
但是3
两次都收到警报,而不是1
then 2
。
有没有办法 pass i
,而不将函数写成字符串?
您必须为每个超时函数安排一个不同的“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
的循环的每个迭代。
您可以使用立即调用的函数表达式 ( IIFE ) 在 周围创建闭包setTimeout
:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
这是因为!
该解决方案的通过使用执行的自功能(匿名一个或更好声明对于每次迭代单个范围IIFE)与具有的副本我在里面,这样的:
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(自执行函数)为每次迭代创建了一个新的作用域,这让我们的超时函数回调有机会为每次迭代关闭一个新的作用域,其中一个变量具有正确的执行其中的迭代值供我们访问。
的函数参数setTimeout
正在关闭循环变量。循环在第一次超时之前完成并显示 的当前值i
,即3
。
由于 JavaScript 变量只有函数作用域,因此解决方案是将循环变量传递给设置超时的函数。您可以像这样声明和调用这样的函数:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
您可以使用setTimeout的额外参数将参数传递给回调函数。
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
注意:这不适用于 IE9 及以下浏览器。