setTimeout 和匿名函数问题
Posted
技术标签:
【中文标题】setTimeout 和匿名函数问题【英文标题】:setTimeout and anonymous function problem 【发布时间】: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++;
【问题讨论】:
【参考方案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);
);
【讨论】:
以上是关于setTimeout 和匿名函数问题的主要内容,如果未能解决你的问题,请参考以下文章