for循环和setTimeout的范围和时序问题。
问题描述:
这应该很容易,但我的头似乎刚刚被弄乱。for循环和setTimeout的范围和时序问题。
我的目标结果是0,1,2,0,1,2
我到目前为止的代码.... here是玩小提琴。
function delayedLoad(page){
console.log(page);
};
function loadContent(){
var i,
len = 3
for (i = 0; i < len; i++) {
console.log(i);
setTimeout(function(){
delayedLoad(i);
},3000*i);
}
};
$(document).ready(function(){
// Load the content for the first time.
loadContent();
});
答
这是一个很常见的问题,人们打了。
麻烦的是,你传递给setTimeout
在每次迭代函数引用相同i
变量。
JavaScript没有块范围,只有函数范围。因此,要创建一个新的范围,将保留您想要的i
值,您需要在循环内调用一个函数,并将其传递给i
。
例子:http://jsfiddle.net/GNwhR/3/
function set_up_timeout(j) {
setTimeout(function(){
delayedLoad(j);
},3000*j);
}
function loadContent(){
var i,
len = 3
for (i = 0; i < len; i++) {
console.log(i);
set_up_timeout(i);
}
};
这会将你的电话setTimeout
中被调用,并在每次迭代通过i
另一个函数。这将创建一个新的变量范围,其中该函数中的j
引用了正确的值。
另一种方法是让调用函数回功能:
例子:http://jsfiddle.net/GNwhR/4/
function set_up_callback(j) {
return function(){
delayedLoad(j);
};
}
function loadContent(){
var i,
len = 3
for (i = 0; i < len; i++) {
console.log(i);
setTimeout(set_up_callback(i), 3000*i);
}
};
这一个电话set_up_callback
,这回报一个函数引用j
。
最后,另一种常见的方法是使用一个IIFE (立即调用函数表达式)消除命名的函数。这是不太清楚IMO:
实施例:http://jsfiddle.net/GNwhR/5/
function loadContent(){
var i,
len = 3
for (i = 0; i < len; i++) {
console.log(i);
setTimeout((function(j){
return function() {
delayedLoad(j);
};
})(i),3000*i);
}
};
这是基本相同的,因为它返回一个函数前面的例子。主要区别在于返回函数的函数在循环内部创建并调用。
所有这些说明了同样的概念,你如果你希望以后异步引用它,并将它保持其预期值需要范围的变量。
答
帕特里克的解释是相当不错的。这里有一些代码可以帮助你解决它。
function delayedLoad(page){
console.log(page);
};
function loadContent(){
var i,
len = 3
for (i = 0; i < len; i++) {
console.log('in loop', i);
setTimeout($.proxy(function(){
delayedLoad(this.page);
}, {page: i}),3000*i);
}
};
$(document).ready(function(){
// Load the content for the first time.
loadContent();
});
jQuery的代理只是重置什么“这”的功能指向..所以,你可以替换传递一个号码,只是重置范围:)(如果让任何意义..长天)
@帕特里克....这是一个非常好的解释。在我等待答案时,我设法解决了这个问题......就像我说的那样应该很简单......我真的不应该在周日晚上编写代码。 –
@詹姆斯:啊,好的。很高兴你明白了。 – user113716