I'm not running into a memory leak in my application yet, but I'm worried about possible problems in the future. I would like to know if doing something like this:
SomeClass.prototype.someMethod= function() {
var that= this
this.$div2.click(function() {
that.someMethod2();
});
}
And lets say that this.$div2 is appended to another div this.$div1. If I call
this.$div1.remove();
and later loses the reference of my SomeClass instance does the SomeClass instance gets garbage collected? And what about the HTML element this.$div2? this.$div2 would not be inside the DOM because it is appended to this.$div1.
I ask this because the event handler in this.$div2 might keep a reference to the HTML element this.$div2 and also keeps a reference to the instance of SomeClass through the closure because of the variable "that".
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
this.$div2
is appended to this.$div1
. If I call this.$div1.remove();
and later lose the reference of my SomeClass
instance does the SomeClass
instance gets garbage collected?
Yes, when all references to it are lost - also those through event handlers , - the instance can get garbage-collected.
And what about the HTML element this.$div2
? this.$div2
would not be inside the DOM because it is appended to this.$div1
.
It does not matter whether it is currently attached to the DOM. If some non-collectible object references $div1
, it also could access its child node $div2
and that one's event handlers, so the instance referenced from the handler would not be collectible.
I ask this because the event handler in this.$div2
might keep a reference to the HTML element this.$div2
and also keeps a reference to the instance of SomeClass
through the closure because of the variable "that".
That's a circular reference and should get handled well by the engines (when none of the objects inside the circle is referenced from outside it can get collected). However, (old?) Internet Explorers fail to do this when a DOM object is involved in the circle.
For that reason the .remove
jQuery method (code) internally calls the (internal) cleanData
method which detaches all event listeners.
So should I care about properly removing all events and HTML elements like this? Or simply removing the "root" element (this.$div1) solves the problem?
Yes, calling remove
on a jQuery wrapper automatically removes all events (from all child elements) and DOM nodes.
Should I care about properly removing all events and HTML elements
like this?
The short answer is No! at least in 99% of the cases, it will not matter in any way because the memory used by one DOM element is trivial compared to the overall memory used by a web page.
However it is always a good practice to release the memory used by disposing unneeded objects, but you cannot say that GC would definitely releases the memory utilized by the elements because garbage collection is entirely up to the browser! In theory GC should only kick in when there are no references to the DOM element, at least that's how Chrome works, but in languages like JavaScript, you don't explicitly tell the run-time you're done with the object, things get messy in JavaScript so quickly: a function might pass the object on to some more functions, the object might get saved away as a member within yet another object, an object might get referenced through the closure etc, so it's completely up to the browser how and what to collect!
In your case removing div1
frees the html document and the element would not render in the view, in fact jQuery's remove
method takes care of removing all the events, expando properties, and child elements attached to the element together with the element itself, however you keep a reference of div1
and div2
in yet another object making both DOM elements Orphan elements! removing SomeClass
instance variable releases all references to the DOM elements making them candidate for garbage collection but here comes the tricky that
variable that causes the DOM element make a reference to the instance of SomeClass
through clusure! This issue is known as Circular Reference
in IE:
JavaScript Objects and DOM elements that store references to one
another cause Internet Explorer’s garbage collector to not reclaim
memory, resulting in memory leaks
You can read more about it here
This particular leak is mostly of historical interest IE<8, but a good example of breaking circular links is to avoid using the variable that
, instead use proxy or delegate to change the event handler's context to some particular context.
ECMA 5th bind method is quit useful changing contexts when in comes to DOM event handlers, here's a simple handler based on your code without using variable closure:
this.$div2.click((function() {
this.someMethod2();
}).bind(this));
If you will create element dynamically, then assign to them events. i think that your code is not a good way to do that. you should follow this manner:
for fixed elements if you need an event, use these two functions; the first called in the constructor, the second in the destructor.
on_Events: function() {
$('your_form').on('event_name', {element_Selector}, callback_function)
},
off_Events: function() {
$('your_form').off('event_name', {element_Selector}, callback_function)
}
for dynamically objects. add events when creating an element and remove these events just before destroying the element.