我需要在DOM事件回调中使用闭包吗

Do I need a closure inside a DOM event callback?

本文关键字:闭包 回调 事件 DOM      更新时间:2023-09-26

我正在尝试构建一个类似jQuery.live的函数。Helper是一个具有_liveEvent_addEventListener方法的类。Helper._addEventListener只是W3C addEventListener的CrossBrowser版本。

Helper.prototype._liveEvent = function(type, evt, ofunc) {
    var elHand = document;
    type = type.toUpperCase();
    this._addEventListener(elHand, evt, function(me) {
        // Inside here I use the `type` variable.
        // I don't know why but it works.
        for (var el = me.srcElement; el.nodeName !== 'HTML';
            el = el.parentNode)
        {
            if (el.nodeName === type || el.parentNode === null) {
                break;
            }
        }
        if (el && el.nodeName === type) {
            ofunc.call(el, me);
        }
    });
};

我用不同的类型运行了两次Helper._liveEvent函数,它运行得很好。我认为由于type变量是在_liveEvent上下文中设置的,所以_addEventListener回调只能看到该变量的最后版本。但事实并非如此,它似乎运行得很好。

我的问题是:

  • 为什么_addEventListener回调可以同时看到类型的两个版本
  • 这是否意味着我的代码正在泄漏内存

更新

另一个例子让我更好地理解了这一点,但我还不确定我是否完全理解。

function foo(i) {
    setTimeout(function() {
        console.log(i);
    }, 400);
}
// Prints 1, 2, 3
for (var i = 1; i < 4; i++) {
    foo(i);
}
function bar() {
    for (var i = 1; i < 4; i++) {
        setTimeout(function() {
            console.log(i);
        }, 400);
    }
}
// Prints 4, 4, 4
bar();
​
  • 这是因为为传递给_addEventListener()的匿名函数的每个实例创建了一个单独的闭包作用域,每个实例都有自己的值elHandtype
  • 这取决于你所说的"泄漏"是什么意思。每个闭包都阻止它所包含的对象GC’ing。当没有更多的对象(比如像你这样的匿名函数)引用闭包时,闭包就被GC’ed了。从这个意义上说,是的,你有内存泄漏,因为你没有办法删除添加的监听器(匿名函数),从而使关联的作用域对象符合GC的条件

实际上,您已经在创建一个闭包。这就是为什么:

for( var i=0; i<10; i++) {
    elem.onclick = (function(id) {alert(id);})(i);
}

works-调用匿名函数将创建一个新的闭包,其中id设置为i当前值。(就我个人而言,我喜欢将参数与我想要使用的变量称为相同的东西,所以我可以将其视为"锁定"该函数的变量值)。

就内存泄漏而言,两次调用不会导致泄漏。如果GC按照我认为的方式工作,它会删除任何没有指向它们的指针的闭包。特别是,当您离开一个页面时,与该页面相关联的任何内存都会被释放。