Circular references - leaking

October 21, 2005

From Quirksmode:

Since these two last scripts are in line with common ways of setting event handlers, while the leaking scripts are very odd and non-real-life, I don't see the problem with event handling. As long as you use simple, common ways of setting event handlers, what can go wrong?

PPK posted the following example (test page) which doesn't leak:

// DOESN'T LEAK

window.onload = init;

function init()
{
  createLinks();
  var x = document.getElementsByTagName('a');
  for (var i=0;i<x.length;i++)
  {
    x[i].onclick = function () {
      this.firstChild.nodeValue = ' Clicked! - ';
    }
  }
}

However, a small change will also make it leak. In this real life scenario we now keep a reference to el to set the className as well.

// LEAK

window.onload = init;

function init()
{
  createLinks();
  var x = document.getElementsByTagName('a');
  for (var i=0;i<x.length;i++)
  {
    var el = x[i];
    el.onclick = function () {
      this.firstChild.nodeValue = ' Clicked! - ';
    };
    el.className = "someClass";
  }
}

This is caused by the reference to el in the scope of the eventhandler. And since this is run in a loop, only x[x.length - 1] is leaked. To make this leak visible, I added a large string to it (test page).

With help of my closure function we can rewrite this to:

// DOESN'T LEAK

window.onload = init;

function init()
{
  createLinks();
  var x = document.getElementsByTagName('a');
  for (var i=0;i<x.length;i++)
  {
    var el = x[i];
    el.onclick = function () {
      this.firstChild.nodeValue = ' Clicked! - ';
    }.closure(el);
    el.className = "someClass";
  }
}

Which doesn't leak (test page).