Joseph the Dreamer Joseph the Dreamer - 2 months ago 10
jQuery Question

How jQuery data() breaks circular reference

I have read an why it's better and how it's implemented. But what i don't really understand is how does it break the circular reference?.

how does it break the reference circle?

$(div1).data('item', div2);
$(div2).data('item', div1);


like example, the divs above point to each other, how is it prevented? I have a hunch, but i just want to make sure if my hunch is right.

Answer

The circular reference problem happens in some browsers when you put a reference to a DOM object on a DOM object as a property on that DOM object. Then, you have two DOM objects pointing at each other. Removing a DOM object with a custom property on it doesn't clear that custom property. A garbage collector that isn't that smart doesn't realize that this DOM reference doesn't count so it gets stuck and there are several ways that this can lead to leaks.

.data() solves this problem because the .data() data is NOT on the DOM object. It's just a javascript data structure that can be associated with the DOM object via a unique string ID.

The one confusing part of this is that when you read with .data("key") and the key isn't found in the javascript .data() data structure, then and only then, jQuery will look for an attribute on the DOM object called "data-key". But whenever you write with .data("key", "myData"), it never writes to the DOM object, only to the javascript data structure.

Thus, since .data() never writes the data to the DOM object, there can't be any of these types of circular references that some browsers have trouble with.

There are some other useful things to know about the .data() data structure. When you use jQuery's .remove() to remove elements from the DOM or when you call $(elem).html("new html"), jQuery clears the .data() data on any removed items. This is one case where it's good not to mix jQuery with plain javascript. If you're using .data(), then you should always remove items from the DOM using jQuery functions so .data() is cleaned up appropriately. Otherwise, you can get memory leaks this way (both the .data() data can leak and any removed DOM objects that are referenced in the .data() can leak. But, if you only use jQuery methods for removing items from the DOM (including the replacing of innerHTML), then jQuery will clean things up appropriately and there will be no leaks.

So, for example, this will create a memory leak:

// suppose elem is a DOM element reference

// store some data in jQuery's data storage on behalf of a DOM element
$(elem).data("someKey", "someValue");

// remove DOM element with plain Javascript
elem.parentNode.removeChild(elem);

Because you removed the DOM element with plain Javascript, jQuery did not have a chance to clean up the data you previously stored. The DOM element itself will be garbage collected, but the .data() value you previously stored is now orphaned in jQuery's storage and is essentially a "leak" as it will likely never be cleared. On the other hand, if you do this:

$(elem).data("someKey", "someValue");
$(elem).remove();

Then, jQuery will see that you're removing the DOM element and will also clear the data you stored with .data().

A fairly simple way to see how it works is to create a couple line script with a non-minimized version of jQuery and then just step through a call to $(elem).data("key", "whatever") in the debugger and watch how it works.