Hey there,

The core source of memory leaks in JS is circular references between the DOM and the JS object space. Essentially this happens when you maintain refs to DOM nodes from your JS *and* put refs to JS objects from the same closure in expando properties of these DOM nodes.

I realise this may sound convoluted :-) Here's a straightforward example:

function SuperDuperWidget(nodeForUI) {
  if (nodeForUI._superDuper) return;
  this.element = nodeForUI; // That's your JS->DOM ref -- "outbound"
  // … some more init
  nodeForUI._superDuper = this; // That's your DOM->JS ref -- "inbound"
}

This sort of pattern is fairly common, and sometimes MUCH hard to spot (you could have indirect circular refs, say jsA -> jsB -> node -> jsA), and this essentially defeats garbage collection on most JS runtimes, which rely to some degree on reference counting.

The solution I like most is to replace DOM->JS refs by JS-side "maps" (or hashtables, or dictionaries, or associative arrays: different names for the same concept). This would look a bit like this (assuming your nodes all have unique IDs, so as to make proper map keys):

var SuperDuperWidget = (function() {
  var nodeToHandlerMap = {};

  return function SuperDuperWidget(nodeForUI) {
    if (nodeToHandlerMap[nodeForUI.id]) return;
    this.element = nodeForUI;
    // … some more init
    nodeToHandlerMap[nodeForUI.id] = this;
  };
})();

The surrounding closure here is there to make sure nodeToHandlerMap is actually private to SuperDuperWidget. Think of it as a static private member for the SuperDuperWidget "class."

I'm a big fan of Prototype myself, so when I use this I usually replace the "nodeForUI.id" in the first check by "$(nodeForUI).identify()" to make sure the node HAS a unique ID (in case it didn't have any before, that is).

Best,

--
Christophe Porteneuve aka TDD
[email protected]

_______________________________________________
JSMentors mailing list
[email protected]
http://jsmentors.com/mailman/listinfo/jsmentors_jsmentors.com

Reply via email to