3

I am currently working in a project with long living pages (LLP), what makes me worry about performance and memory leaks.

Revisiting some material at MDN, I've discovered that this could lead to a memory leak:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    };
}

At first I did not understand why, but was easy to find a lot of explanations for this. I've found an article at codeproject, a more detailed explanation in this question and this article from Douglas Crockford.

Now my question is:

As far as I understood, this specific leak is an IE-only bug, is that correct? And, more important: until which version of our beloved blue browser from uncle Bill's company should I be worried about with this leak?

Edit:

Reliable references about the last question are desirable.

Edit #2:

Maybe I am wrong, but I think that using the 'new' standardized interface for events, we would not have the same problem:

function addHandler() {
    var el = document.getElementById('el');
    el.addEventListener('click', function() {
        this.style.backgroundColor = 'red';
    });
}

Since there is no direct assignment, I think there this solves the problem. However, this API is only IE9+, what makes me wonder if we would have the same problem with libraries' fallbacks, such as jQuery:

var $el = $('.some-el');
$el.on('click', function(){
    // ...
});

I do not know enough about jQuery internals to say what this piece of code does on IE8-.

Community
  • 1
  • 1
Henrique Barcelos
  • 7,670
  • 1
  • 41
  • 66
  • 2
    [This MSDN blog post](http://blogs.msdn.com/b/gpde/archive/2009/08/03/javascript-memory-leak-detector-v2.aspx) claims IE8 "mitigated" this problem. Whether or not this is the case... Here's a [*question*](http://stackoverflow.com/questions/11186750/memory-leak-risk-in-javascript-closures) (not an *answer*) with a fair explanation that also claims IE8 only has a minor variant of the problem. – Jared Farrish Aug 01 '14 at 02:09
  • I would think also that jQuery would use the native `elem.attachEvent()` variant that existed in IE only until recently when `elem.addEventListener()` support was added. – Jared Farrish Aug 01 '14 at 02:12
  • I'm not entirely sure the _MDN_ here is correct; the handler uses `this` and not `el` – Paul S. Aug 01 '14 at 02:17
  • @PaulS., yes, but the outter scope is automatically 'added' to the closure scope, creating a reference to `el` either way. – Henrique Barcelos Aug 01 '14 at 02:21
  • Using *addEventListener* doesn't fix the issue because the closure is still formed as a consequence of the assignment of a function expression from within a function context creates a closure and circular reference. Also, the version of IE that mattered (early IE 6) didn't support *addEventListener* anyway. But substituting *attachEvent* doesn't make any difference. – RobG Aug 01 '14 at 02:22
  • @HenriqueBarcelos It must only effect very old browsers if they don't realise the inner function can never perform a lookup of `el`. Modern browsers will see no `el` and see no silly thing like `eval` and go "yay no more references!" – Paul S. Aug 01 '14 at 02:26
  • @RobG, there will be a reference `closure --> element`, but will there still be a reference `element --> closure`? I don't know specifics of this event handling API, but it seems a lot like a *Mediator Pattern* implementation. – Henrique Barcelos Aug 01 '14 at 02:26
  • @HenriqueBarcelos There is the reference `element --> function` and the _function_ has it's associated `closure (A)`, so the _element can keep the closure alive_. There is `closure (A) --> parent closure (B) --> element` so a "dumb" GC would find this a loop and never clean up. However, because the _function_ of `closure (A)` **contains no _identifier_ token** which references `element`, a "smart" GC would see `element` is now impossible to access from `closure (A)` and clean up after the _function_ of `parent closure (B)` has finished it's invocation. – Paul S. Aug 01 '14 at 02:45
  • The real problem with memory leaking happens when you have the form `el.prop = function () {el;}`, in this case, `el` keeps the _function_ alive and the _function_ directly keeps `el` alive, and this is the format that you see talked about in your explanation links, it's similar to a reference like `foo.bar = foo` but because _IE_ has a completely separate engine for the _DOM_ to it's _JavaScript_ engine _JScript_, the older versions (esp. IE6, 7) were unable to communicate with eachother when an `element` was being destroyed, so the GC never knew "all references are now circular or dead" – Paul S. Aug 01 '14 at 02:49
  • @PaulS., but `el` and `this` inside the function scope are the same variable. – Henrique Barcelos Aug 01 '14 at 02:51
  • 2
    @HenriqueBarcelos [**`this`**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) is a completely different mechanism in _JavaScript_ and gets computed **at invocation time** by **how it is invoked**, not how or where the function is defined. i.e. while `foo.bar = function () {return this;}` invoked as `foo.bar();` is `foo`, you also have `fizz.buzz = foo.bar; fizz.buzz(); // fizz` and `foo.bar.call(fizz); // fizz` – Paul S. Aug 01 '14 at 02:55
  • @PaulS., about the references, what you said is not necessarily true. With a Mediator Pattern-based implementation, it's possible to mantain the references to the callbacks only within the Mediator and just associate them with the element when the event is triggered using `apply/call` – Henrique Barcelos Aug 01 '14 at 02:56

0 Answers0