Internet Explorer and Mozilla Firefox are two browsers that use reference counting to handle memory for DOM objects. In a reference counting system, each object referenced maintains a count of how many objects are referencing it. If the count becomes zero, the object is destroyed and the memory is returned to the heap. Although this solution is generally very efficient, it has a blind spot when it comes to circular (or cyclic) references.
What's wrong with circular references?A circular reference is formed when two objects reference each other, giving each object a reference count of 1. In a purely garbage collected system, a circular reference is not a problem: If neither of the objects involved is referenced by any other object, then both are garbage collected. In a reference counting system, however, neither of the objects can be destroyed, because the reference count never reaches zero. In a hybrid system, where both garbage collection and reference counting are being used, leaks occur because the system fails to identify a circular reference. In this case, neither the DOM object nor the JavaScript object is destroyed. Listing 1 shows a circular reference
between a JavaScript object and a DOM object.
<html>
<body>
<script type="text/javascript">
document.write("Circular references between JavaScript and DOM!");
var obj;
window.onload = function(){
obj=document.getElementById("DivElement");
document.getElementById("DivElement").expandoProperty=obj;
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
};
</script>
<div id="DivElement">Div Element</div>
</body>
</html>
As you can see in the above listing, the JavaScript object obj has a reference to the DOM object represented by DivElement. The DOM object, in turn, has a reference to the JavaScript object through the expandoProperty. A circular reference exists between the JavaScript object and the DOM object. Because DOM objects are managed through reference counting, neither object will ever be destroyed.
Learning about closures
<html>
<body>
<script type="text/javascript">
document.write("Closure Demo!!");
window.onload=
function closureDemoParentFunction(paramA)
{
var a = paramA;
return function closureDemoInnerFunction (paramB)
{
alert( a +" "+ paramB);
};
};
var x = closureDemoParentFunction("outer x");
x("inner x");
</script>
</body>
</html>
In the above listing closureDemoInnerFunction is the inner function defined within the parent function closureDemoParentFunction. When a call is made to closureDemoParentFunction with a parameter of outer x, the outer function variable a is assigned the value outer x. The function returns with a pointer to the inner function closureDemoInnerFunction, which is contained in the variable x.
It is important to note that the local variable a of the outer function closureDemoParentFunction will exist even after the outer function has returned. This is different from programming languages such as C/C++, where local variables no longer exist once a function has returned. In JavaScript, the moment closureDemoParentFunction is called, a scope object with property a is created. This property contains the value of paramA, also known as "outer x". Similarly, when the closureDemoParentFunction returns, it will return the inner function closureDemoInnerFunction, which is contained in the variable x.
Because the inner function holds a reference to the outer function's variables, the scope object with property a will not be garbage collected. When a call is made on x with a parameter value of inner x -- that is, x("inner x") -- an alert showing "outer x innerx" will pop up.
Closures and circular referencesIn Listing 5 you see a closure in which a JavaScript object (obj) contains a reference to a DOM object (referenced by the id "element"). The DOM element, in turn, has a reference to the JavaScript obj(innerFunction). The resulting circular reference between the JavaScript object and the DOM object causes a memory leak.
<html>
<body>
<script type="text/javascript">
document.write("Program to illustrate memory leak via closure");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction(){
alert("Hi! I will leak");
};
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
// This is used to make the leak significant
};
</script>
<button id="element">Click Me</button>
</body>
</html>
Avoiding memory leaksListing 6. Break the circular reference
<html>
<body>
<script type="text/javascript">
document.write("Avoiding memory leak via closure by breaking the circular
reference");
window.onload=function outerFunction(){
var obj = document.getElementById("element");
obj.onclick=function innerFunction()
{
alert("Hi! I have avoided the leak");
// Some logic here
};
obj.bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
obj = null; //This breaks the circular reference
};
</script>
<button id="element">"Click Here"</button>
</body>
</html>
Listing 7. Add another closure
<html>
<body>
<script type="text/javascript">
document.write("Avoiding a memory leak by adding another closure");
window.onload=function outerFunction(){
var anotherObj = function innerFunction()
{
// Some logic here
alert("Hi! I have avoided the leak");
};
(function anotherInnerFunction(){
var obj = document.getElementById("element");
obj.onclick=anotherObj })();
};
</script>
<button id="element">"Click Here"</button>
</body>
</html>
Listing 8. Avoid the closure altogether
<html>
<head>
<script type="text/javascript">
document.write("Avoid leaks by avoiding closures!");
window.onload=function()
{
var obj = document.getElementById("element");
obj.onclick = doesNotLeak;
}
function doesNotLeak()
{
//Your Logic here
alert("Hi! I have avoided the leak");
}
</script>
</head>
<body>
<button id="element">"Click Here"</button>
</body>
</html>