繁体   English   中英

Javascript:如何将不同的对象传递给在循环中创建的setTimeout处理程序?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM