setTimeout和匿名函数问题

时间:2010-01-31 12:12:44

标签: javascript

这是我的代码,用错误的值调用SetOpacity,为什么?

function SetOpacity(eID, opacity){                  
   eID.style.opacity = opacity / 100;
   eID.style.filter = 'alpha(opacity=' + opacity + ')';
}
function fade(eID, startOpacity, endOpacity){           
    var timer = 0;
    if (startOpacity < endOpacity) { 
       for (var i = startOpacity; i <= endOpacity; i++) {
           setTimeout(function() {SetOpacity(eID, i);}, timer * 30);
           timer++;
        }
    }           
}

4 个答案:

答案 0 :(得分:43)

这应该有效:

for (var i = startOpacity; i <= endOpacity; i++) {
    (function(opacity) {
        setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
    })(i);
    timer++;
}

其工作原理如下:

  • 在内部循环中创建匿名函数(function(...){...})并立即使用参数调用它(这就是function(){}周围有括号的原因,因此您可以在结尾处调用它添加()并传递参数)
  • 传递给此匿名函数的参数(i值,函数内部为opacity)是此匿名函数的本地参数,因此它们不会在循环的后续步骤中更改,并且您可以保密将它们传递给另一个匿名函数(在setTimeout

您的原始版本无效,因为:

  • 传递给setTimeout的函数保存对变量i的引用(不是它的值),并在调用此函数时获取值,而不是在将其添加到{{1 }}
  • 此变量的值在循环中更改,在您获得第一个setTimeout之前,它获得setTimeout值(来自endOpacity循环的最后一个值)

不幸的是JavaScript只有函数作用域,所以如果你在循环中创建变量并分配新的实际值它将不起作用,因为只要函数内部有for,那些变量就会在函数时创建执行(默认情况下获取var值)。创建新范围的唯一(简单)方法是创建函数(可能是匿名的)并在其中创建新变量(参数也是变量)

答案 1 :(得分:5)

这是一个关闭问题。当您运行该功能时,i已经在endOpacity。这可以通过创建另一个闭包来实现:

function SetOpacityTimeout(eID, opacity, timer){
  setTimeout(function() {SetOpacity(eID, opacity);}, timer * 30);
}

function fade(eID, startOpacity, endOpacity){           
    var timer = 0;
    if (startOpacity < endOpacity) {
       for (var i = startOpacity; i <= endOpacity; i++) {
          SetOpacityTimeout(eID,i,timer);
          timer++;
        }
    }           
}

答案 2 :(得分:1)

Kobi对这个问题有正确的想法。不过,我建议您使用间隔。

这是一个例子:(你的SetOpacity函数保持不变,我把它留在这里。)

function fade(eID, startOpacity, endOpacity){
    var opacity = startOpacity;
    SetOpacity(eID, opacity);

    var interval = window.setInterval(function(){
        opacity++;
        SetOpacity(eID, opacity);

        // Stop the interval when done
        if (opacity === endOpacity)
            window.clearInterval(interval);
    }, 30);
}

答案 3 :(得分:1)

这是我与jquery一起使用的例子。 “menuitem”是itemclass,jquery检查“recentOut”类,看它是否需要向上滑动。

代码说明了一切。

$(".menuitem").mouseenter(
function(){
    $(this).addClass("over").removeClass("out").removeClass("recentlyOut");
    $(this).children(".sub").slideDown();
}); 
    $(".menuitem").mouseleave(
function(){

    $(this).addClass("out").addClass("recentlyOut").removeClass("over");
    setTimeout(function()
        {
            var bool = $(".recentlyOut").hasClass("over");
            if (!bool)
            {
    $(".recentlyOut").removeClass("recentlyOut").children(".sub").slideUp();
            }
        }
    , 400);
}
    );