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

发布于 2024-09-14 07:21:29 字数 1161 浏览 10 评论 0原文

我正在尝试编写一些 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

打小就很酷 2024-09-21 07:21:29

你的“另一种方法”是正确的,这就是通常的做法。

至于 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 函数不再绑定变量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);
}

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 (like function() { alert(i); }), that function, as they say, 'captures', or 'binds' the variable i, so that variable i 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:

var i = 5;
var fn = function() { alert(i); };

fn();  // displays "5"

i = 6;
fn();  // displays "6"

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:

function createFn( theArgument )
{
    return function() { alert( theArgument ); };
}

var i = 5;
var fn = createFn( i );

fn();  // displays "5"

i = 6;
fn();  // still displays "5". Voila!

This works, because the fn function no longer binds the variable i. Instead, now it binds another variable - theArgument, which has nothing to do with i, other than they have the same value at the moment of calling createFn. Now you can change your i all you want - theArgument will be invincible.

Applying this to your code, here's how you should modify it:

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);
}
涫野音 2024-09-21 07:21:29

第一种方法是在运行时评估代码。您很可能对失败的原因是正确的(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 of eval() (and setTimeout(string, ...) is a form of eval()) 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 the fadeIn function's scope.

When you later run the function, it uses that reference to refer back to the i from fadeIn's scope. By the time this happens however, the loop is over so you'll forever just get i 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文