Javascript:如何将不同的对象传递给循环中创建的 setTimeout 处理程序?
我正在尝试编写一些 JS 来复制 jQuery 的 fadeIn 和 fadeOut 函数。这是我到目前为止的代码:
function fadeIn(elem, d, callback)
{
var duration = d || 1000;
var steps = Math.floor(duration / 50);
setOpacity(elem,0);
elem.style.display = '';
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout('setOpacity("elem", '+(i / steps)+' )', (i/steps) * duration);
}
if (callback)
setTimeout(callback,d);
}
function setOpacity(elem, level)
{
console.log(elem);
return;
elem.style.opacity = level;
elem.style.MozOpacity = level;
elem.style.KhtmlOpacity = level;
elem.style.filter = "alpha(opacity=" + (level * 100) + ");";
}
我在第一次 setTimeout 调用时遇到了麻烦 - 我需要将对象“elem”(这是一个 DOM 元素)传递给函数 setOpacity。传递“level”变量效果很好...但是,我收到“elem 未定义”错误。我认为这是因为当任何 setOpacity 调用实际运行时,初始 fadeIn 函数已经完成,因此变量 elem 不再存在。
为了缓解这个问题,我尝试了另一种方法:
setTimeout(function() { setOpacity(elem, (i / steps));}, (i/steps) * duration);
现在的问题是,当调用该函数时,(i/steps) 现在始终为 1.05,而不是从 0 递增到 1。
如何在正确单步执行时将有问题的对象传递给 setOpacity提高不透明度水平?
I'm trying to write some JS replicating jQuery's fadeIn and fadeOut functions. Here's the code I have so far:
function fadeIn(elem, d, callback)
{
var duration = d || 1000;
var steps = Math.floor(duration / 50);
setOpacity(elem,0);
elem.style.display = '';
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout('setOpacity("elem", '+(i / steps)+' )', (i/steps) * duration);
}
if (callback)
setTimeout(callback,d);
}
function setOpacity(elem, level)
{
console.log(elem);
return;
elem.style.opacity = level;
elem.style.MozOpacity = level;
elem.style.KhtmlOpacity = level;
elem.style.filter = "alpha(opacity=" + (level * 100) + ");";
}
I'm having troubles with the first setTimeout call - I need to pass the object 'elem' (which is a DOM element) to the function setOpacity. Passing the 'level' variable works just fine... however, I'm getting "elem is not defined" errors. I think that's because by the time any of the setOpacity calls actually run, the initial fadeIn function has finished and so the variable elem no longer exists.
To mitigate this, I tried another approach:
setTimeout(function() { setOpacity(elem, (i / steps));}, (i/steps) * duration);
The trouble now is that when the function is called, (i/steps) is now always 1.05 instead of incrementing from 0 to 1.
How can I pass the object in question to setOpacity while properly stepping up the opacity level?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你的“另一种方法”是正确的,这就是通常的做法。
至于
i
始终是常量的问题,这就是闭包的工作原理!您会看到,当您创建这个使用
i
执行某些操作的函数(例如function() {alert(i); }
)时,该函数正如他们所说,'捕获',或'绑定'变量i
,这样变量i
在循环结束后不会消亡,但继续存在并且仍然被该函数引用。为了演示这个概念,请考虑以下代码:
当以这种方式编写时,这个概念变得更加明显,不是吗?由于您要更改循环中的变量,因此在循环完成后,变量将保留
(1+steps)
的最后一个值 - 这正是您的函数在开始执行时看到的内容。要解决此问题,您必须创建另一个将返回函数的函数。是的,我知道,有点令人兴奋,但请耐心听我说。考虑我的示例的修订版本:
这有效,因为
fn
函数不再绑定变量i
。相反,现在它绑定了另一个变量 -theArgument
,它与i
无关,只是它们在调用createFn
时具有相同的值。代码>.现在你可以随心所欲地改变你的i
-theArgument
将是无敌的。将此应用到您的代码中,您应该如何修改它:
Your "another approach" is correct, this is how it's usually done.
And as for the problem of
i
always being a constant, that's how closures work!You see, when you create this function that does something with
i
(likefunction() { alert(i); }
), that function, as they say, 'captures', or 'binds' the variablei
, so that variablei
does not die after the loop is finished, but continues to live on and is still referenced from that function.To demonstrate this concept, consider the following code:
When it is written in this way, the concept becomes a bit more evident, doesn't it? Since you're changing the variable in the loop, after the loop is finished the variable retains it's last value of
(1+steps)
- and that's exactly what your function sees when it starts executing.To work around this, you have to create another function that will return a function. Yes, I know, kind of mind-blowing, but bear with me. Consider the revised version of my example:
This works, because the
fn
function no longer binds the variablei
. Instead, now it binds another variable -theArgument
, which has nothing to do withi
, other than they have the same value at the moment of callingcreateFn
. Now you can change youri
all you want -theArgument
will be invincible.Applying this to your code, here's how you should modify it:
第一种方法是在运行时评估代码。您很可能对失败的原因是正确的(
elem
不在代码评估的范围内)。使用任何形式的eval()
(而setTimeout(string, ...)
是eval()
的一种形式)通常是不好的Javascript 中的想法,最好像第二种方法一样创建一个函数。要理解第二种方法失败的原因,您需要了解范围和具体的闭包。当您创建该函数时,它会从
fadeIn
函数的作用域中获取对i
变量的引用。当您稍后运行该函数时,它会使用该引用从
fadeIn
的范围引用回i
。然而,当这种情况发生时,循环就结束了,所以你将永远得到i
循环结束时的值。你应该做的是重新设计它,而不是一次创建许多 setTimeouts (这是低效的),而是告诉你的 setTimeout 回调函数设置下一个 Timeout (或者你可以使用 setInterval),并在你的值在里面时进行递增那个回调函数。
Your first approach is evaluating code at runtime. You are most likely right about why it's failing (
elem
is not in the scope in which the code is eval'd). Using any form ofeval()
(andsetTimeout(string, ...)
is a form ofeval()
) is a general bad idea in Javascript, it's much better to create a function as in your second approach.To understand why your second approach is failing you need to understand scopes and specifically closures. When you create that function, it grabs a reference to the
i
variable from thefadeIn
function's scope.When you later run the function, it uses that reference to refer back to the
i
fromfadeIn
's scope. By the time this happens however, the loop is over so you'll forever just geti
being whatever it was when that loop ended.What you should do is re-engineer it so that instead of creating many setTimeouts at once (which is inefficient) you instead tell your setTimeout callback function to set the next Timeout (or you could use setInterval) and do the incrementing if your values inside that callback function.