[英]Javascript: how to pass different object to setTimeout handlers created in a loop?
我正在尝试编写一些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'变量很好用...但是,我收到“未定义元素”错误。 我认为这是因为在实际运行任何setOpacity调用时,初始fadeIn函数已经完成,因此变量elem不再存在。
为了减轻这种情况,我尝试了另一种方法:
setTimeout(function() { setOpacity(elem, (i / steps));}, (i/steps) * duration);
现在的麻烦在于,调用该函数时,(i / steps)现在始终为1.05,而不是从0递增到1。
如何在适当提高不透明度级别的同时将有问题的对象传递给setOpacity?
您的“另一种方法”是正确的,这是通常的做法。
至于i
一直是一个常数的问题,那就是闭包是如何工作的! 您会看到,当创建此函数对i
(例如function() { alert(i); }
),该函数就像他们所说的那样, “捕获”或“绑定”了变量i
,因此变量i
不会在循环结束后死亡,而是继续存在并仍从该函数引用。
为了演示此概念,请考虑以下代码:
var i = 5;
var fn = function() { alert(i); };
fn(); // displays "5"
i = 6;
fn(); // displays "6"
当以这种方式编写时,这个概念变得更加明显了,不是吗? 由于您要在循环中更改变量,因此在循环完成后,变量将保留其最后一个值(1+steps)
-这正是函数开始执行时所看到的。
若要解决此问题,您必须创建另一个函数,该函数将返回一个函数。 是的,我知道,有点令人惊讶,但请忍受我。 考虑我的示例的修订版:
function createFn( theArgument )
{
return function() { alert( theArgument ); };
}
var i = 5;
var fn = createFn( i );
fn(); // displays "5"
i = 6;
fn(); // still displays "5". Voila!
这是fn
,因为fn
函数不再绑定变量i
。 相反,它现在绑定了另一个变量theArgument
,它与i
无关,除了在调用createFn
它们具有相同的值。 现在,你可以改变你i
所有你想要的- theArgument
将立于不败之地。
将其应用到您的代码中,下面是修改它的方法:
function createTimeoutHandler( elemArg, iDivStepsArg )
{
return function() { setOpacity( elemArg, iDivStepsArg ); };
}
for (var i = 1; i <= steps; i++)
{
console.log(i/steps + ', ' + (i/steps) * duration);
setTimeout( createTimeoutHandler( elem, i/steps ), (i/steps) * duration);
}
您的第一种方法是在运行时评估代码。 关于失败的原因,您很可能是正确的( elem
不在评估代码的范围内)。 使用任何形式的eval()
(而setTimeout(string, ...)
是eval()
的形式eval()
在Javascript中通常是一个坏主意,最好像在第二种方法中那样创建一个函数。
要了解第二种方法失败的原因,您需要了解范围,尤其是闭包。 创建该函数时,它会从fadeIn
函数的作用域中获取对i
变量的引用。
当您稍后运行该函数时,它将使用该引用从fadeIn
的作用域返回引用i
。 但是,当这种情况发生时,循环结束了,所以您永远都可以使i
成为循环结束时的状态。
您应该做的是对其进行重新设计,以便与其立即创建多个setTimeouts(效率低下),而不是告诉setTimeout回调函数设置下一个Timeout(或者您可以使用setInterval),如果值在内部,则进行递增该回调函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.