【问题标题】:setTimeout and anonymous function problemsetTimeout 和匿名函数问题
【发布时间】:2011-01-11 09:52:58
【问题描述】:

这是我的代码,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++;
        }
    }           
}

【问题讨论】:

    标签: javascript


    【解决方案1】:

    这应该可行:

    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引用(不是它的),并且它仅在以下情况下解析其值这个函数被调用了,不是在添加到setTimeout的时候
    • 这个变量的值在循环中被改变,甚至在第一个setTimeout执行之前,i将达到endOpacity(来自for循环的最后一个值)

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

    【讨论】:

    • +1 - 我也开始使用匿名函数。再看一遍,我猜你的更优雅。
    • 你能用 (i) 解释一下吗?
    【解决方案2】:

    这是一个关闭问题。当您运行该函数时,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++;
            }
        }           
    }
    

    【讨论】:

    • 你测试过这个吗? var opacity 仍与 i 在同一范围内,因此据我所知,它应该仍然中断。
    【解决方案3】:

    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);
    }
    

    【讨论】:

      【解决方案4】:

      这是我与 jquery 一起使用的示例。 "menuitem" 是 itemclass,jquery 检查 "recentlyOut" 类看它是否需要向上滑动。

      代码不言自明。

      $(".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);
      }
          );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多