Javascript setTimeout 和匿名函数问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2171602/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:04:29  来源:igfitidea点击:

setTimeout and anonymous function problem

javascript

提问by ronik

This is my code, SetOpacity get invoked with wrong values, why?

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

回答by MBO

This should work:

这应该有效:

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

This works as follows:

其工作原理如下:

  • inside the loop you create an anonymous function (function(...){...}) and immediately call it with a parameter (that's why there are parentheses around function(){}, so you can call it by adding ()at the end and passing parameters)
  • parameters passed to this anonymous function (in this case i, which is opacityinside the function) are local to this anonymous function, so they don't change during the next iterations of the loop, and you can safely pass them to another anonymous function (the first parameter in setTimeout)
  • 在循环内创建一个匿名函数 ( function(...){...}) 并立即使用参数调用它(这就是为什么周围有括号的原因function(){},因此您可以通过()在末尾添加并传递参数来调用它)
  • 传递给这个匿名函数的参数(在这种情况下i,它opacity在函数内部)是这个匿名函数的本地参数,所以它们在循环的下一次迭代中不会改变,你可以安全地将它们传递给另一个匿名函数(中的第一个参数setTimeout

Your original version didn't work because:

您的原始版本不起作用,因为:

  • your function that is passed to setTimeoutholds a referenceto the variable i(not the valueof it), and it resolves its value only when this function is called, which is not at the time of adding it to setTimeout
  • the value of this variable gets changed in the loop, and before even the first setTimeoutexecutes, iwill have reached endOpacity(the last value from the forloop)
  • 传递给的函数setTimeout包含对变量的引用i(而不是它的),并且仅在调用此函数时才解析其值,而不是在将其添加到setTimeout
  • 这个变量的值在循环中被改变,甚至在第一个setTimeout执行之前,i将达到endOpacityfor循环中的最后一个值)

Unfortunately JavaScript only has function scope, so it won't work if you create the variable inside the loop and assign a new actual value, because whenever there is some varinside a function, those variables are created at the time of function execution (and are undefinedby default). The only (easy) way to create new scope is to create a function (which may be anonymous) and create new variables inside it (parameters are variables too).

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

回答by Kobi

This is a closure issue. By the time you run the function, iis already at endOpacity. This will work, by creating another closure:

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

回答by Stéphan Kochen

Kobi has the right idea on the problem. I suggest you use an interval instead, though.

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

Here's an example: (Your SetOpacity function remains the same, I left it out here.)

这是一个示例:(您的 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);
}

回答by Josh

This is am example I used with jquery. "menuitem" is the itemclass and jquery checks the "recentlyOut" class to see if it needs to slide back up.

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

The code speaks for itself.

代码不言自明。

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