Eduardo
Eduardo

Reputation: 22834

Do I need a closure inside a DOM event callback?

I'm trying to build a jQuery.live like function. Helper is a class that has the _liveEvent and _addEventListener methods. Helper._addEventListener is just a CrossBrowser version of W3C addEventListener.

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);
        }

    });
};

I'm running the Helper._liveEvent function twice with different types, and it works just fine. I thought that since the type variable was set inside the _liveEvent context the _addEventListener callback could see only the last version of that variable. But it's not the case, it seems to be working fine.

My questions are:

UPDATE

This other example made me understand this better, but I'm not sure I understand it fully yet.

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();
​

Upvotes: 0

Views: 122

Answers (2)

Alexander Pavlov
Alexander Pavlov

Reputation: 32286

  • It's because a separate closure scope is created for every instance of the anonymous function passed to _addEventListener(), each having its own values of elHand and type.
  • It depends on what you mean by "leaking". Every closure prevents objects that it contains from GC'ing. A closure is GC'ed when there are no more objects (say, anonymous functions like yours) referencing it. In this sense, yes, you have a memory leak, as you have no way to remove the added listener (anonymous function) thereby making the associated scope object eligible for GC.

Upvotes: 1

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324650

Effectively, you already are creating a closure. This is why:

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

works - calling the anonymous function creates a new closure with id set to the current value of i. (Personally I like to call the argument the same thing as the variable I want to use, so I can think of it as "locking" the value of the variable for that function).

As far as memory leaks go, two calls is not going to cause a leak. If GC works the way I think it does, it removes any closures that have no pointers to them. In particular, when you leave a page, any memory associated with that page is freed.

Upvotes: 1

Related Questions