for循环和setTimeout的范围和时间问题。

时间:2011-08-28 19:03:52

标签: javascript scope

这个应该很容易,但我的脑袋现在似乎已经疲惫不堪了。

我的目标是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();
}); 

2 个答案:

答案 0 :(得分:3)

这是人们遇到的一个非常普遍的问题。

麻烦的是,您在每次迭代中传递给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);   

    }
};

这与前一个示例基本相同,因为它返回一个函数。主要区别在于返回函数的函数是在循环内部创建和调用的。

所有这些都说明了相同的概念,如果您希望稍后异步引用它并使其保持其预期值,则需要对变量进行范围调整。

答案 1 :(得分:1)

帕特里克的解释非常正确。这里有一些代码可以帮助您解决它。

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的代理只是重置了'this'在函数中指向的东西..所以,你可以替换一个数字中的传递来重置范围:)(如果这有意义......漫长的一天)