我正在编写一个函数来触发一系列点击处理程序,它们之间有一定的时间间隔。
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
b.click();
}, 300 + (step*i), buttons[i]);
}
}
上述功能按预期工作,但是,以下版本对我来说似乎应该是等效的,不起作用:
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
console.log(i);
console.log(b);
b[i].click();
}, 300 + (step*i), buttons);
}
}
我收到以下错误:
TypeError: Cannot read property 'click' of undefined
检查后,我发现console.log(i)正在打印6.所以,显然,直到循环结束后才发生属性访问,这解释了错误!但到底是怎么回事?我对javascript比较新,但是这个行为是匿名函数作为闭包的结果吗?这对我来说听起来不是正确的解释。是因为setTimeout延迟了对匿名函数的评估吗?
ETA:
我做了一个实验:
function sleep(ms){
var current = new Date().getTime();
while (current + ms >= new Date().getTime()){};
}
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
console.log(b);
console.log(i);
b[i].click();
}, 300 + (step*i), buttons);
sleep(1000);
}
}
当我运行play(200)时,我收到相同的错误消息,所以sleep(1000)应该有足够的时间确保在第一次超时之前循环没有退出,不是吗?
答案 0 :(得分:2)
因为for循环中的变量在每次迭代中都是相同的(相同的引用),并且当setTimeout运行时,for循环已经结束,所以i
变量将是循环的最后一个值,到修复此问题,您可以使用i
变量创建一个闭包:
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
(function(i) {
setTimeout(function(b){
console.log(i);
console.log(b);
b[i].click();
}, 300 + (step*i), buttons);
})(i);
}
}