我有这个脚本:
for (var i = 1; i <= 2; i++) { setTimeout(function() { alert(i) }, 100); }
但是3两次都被提醒,而不是1then 2。
3
1
2
有没有办法通过i,而不把函数写成字符串?
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 毫秒后调用此函数”几乎会立即返回,因为在定时器队列中安装超时请求的过程非常快。
setTimeout()
因此,如果发出 一连串 超时请求,就像 OP 中的代码和我的回答中的情况一样,并且每个请求的时间延迟值相同,那么一旦该时间量过去,所有计时器处理程序会一个接一个地被快速接连调用。
如果您需要每隔一段时间调用处理程序,您可以使用setInterval(),它的调用方式与调用完全相同,setTimeout()但在请求数量的重复延迟后会触发多次,或者您可以建立超时并乘以时间迭代计数器的值。也就是说,要修改我的示例代码:
setInterval()
function doScaledTimeout(i) { setTimeout(function() { alert(i); }, i * 5000); }
(用100毫秒超时,效果不会很明显,所以我把这个数字提高到了5000。)的值i乘以基本延迟值,所以在循环中调用5次将导致延迟为5秒、10 秒、15 秒、20 秒和 25 秒。
100
更新
在 2018 年,有一个更简单的选择。有了在比函数更窄的范围内声明变量的新能力,如果这样修改原始代码将可以工作:
for (let i = 1; i <= 2; i++) { setTimeout(function() { alert(i) }, 100); }
与let不同,声明var本身将导致i循环的每次迭代都有一个不同的。
let
var