Precise explanation of JavaScript <-> DOM circular reference issue
- by Joey Adams
One of the touted advantages of jQuery.data versus raw expando properties (arbitrary attributes you can assign to DOM nodes) is that jQuery.data is "safe from circular references and therefore free from memory leaks". An article from Google titled "Optimizing JavaScript code" goes into more detail:
The most common memory leaks for web applications involve circular
references between the JavaScript script engine and the browsers' C++
objects' implementing the DOM (e.g. between the JavaScript script
engine and Internet Explorer's COM infrastructure, or between the
JavaScript engine and Firefox XPCOM infrastructure).
It lists two examples of circular reference patterns:
DOM element → event handler → closure scope → DOM
DOM element → via expando → intermediary object → DOM element
However, if a reference cycle between a DOM node and a JavaScript object produces a memory leak, doesn't this mean that any non-trivial event handler (e.g. onclick) will produce such a leak? I don't see how it's even possible for an event handler to avoid a reference cycle, because the way I see it:
The DOM element references the event handler.
The event handler references the DOM (either directly or indirectly). In any case, it's almost impossible to avoid referencing window in any interesting event handler, short of writing a setInterval loop that reads actions from a global queue.
Can someone provide a precise explanation of the JavaScript ↔ DOM circular reference problem? Things I'd like clarified:
What browsers are effected? A comment in the jQuery source specifically mentions IE6-7, but the Google article suggests Firefox is also affected.
Are expando properties and event handlers somehow different concerning memory leaks? Or are both of these code snippets susceptible to the same kind of memory leak?
// Create an expando that references to its own element.
var elem = document.getElementById('foo');
elem.myself = elem;
// Create an event handler that references its own element.
var elem = document.getElementById('foo');
elem.onclick = function() {
elem.style.display = 'none';
};
If a page leaks memory due to a circular reference, does the leak persist until the entire browser application is closed, or is the memory freed when the window/tab is closed?