Close on Closures

Close on Closures

When discussing the prototype object, it’s necessary to consider JavaScript closures. The prototype is used to extend the definition of an object, regardless of what other scope is in effect. The prototype is available as a characteristic of the type, regardless of whether you even have an instance of the object. The prototype is independent from the state during which it was created. Closures, on the other hand, are used to leverage the scope in play at the time the closure is created. A closure refers to how an inner function can be defined that can take advantage of the state of the outer function’s context and be made visible externally so it can be executed after the outer function has returned. The behavior of the closure can depend on the state and scope that existed when the closure was created. Scoping behavior is the same in JavaScript as it is in C# and VB.NET, with one notable exception as was shown in Figure 3-1: Using a variable without declaring it gives it a global scope.

If you search for information on closures, you are bound to find some hard-to-follow, overly complex scientific explanations. Concrete specific examples are generally better at explaining closures than formal definitions. The prototype can reference properties and functions using the this operator, but the lexical scoping means that any outside variables that happen to exist do not come into play. It is typically an error to try and use transient state information in a prototype extension. The various instances of the object type being extended shouldn’t leverage any other scope that happens to be in existence, unless it is deliberate access to global information. A closure function captures the state of its scope at the time it is created.

As I’ve stated, all of this talk about scoping is much easier to see in an example. Listing 3-14 (from clo- sure.htm) contains a function that creates another function. The function created just squares the variable according to the scope that was in effect when the closure was created. The code loops from 0 to 10, calling the function creator and storing the results. Then another loop goes through the stored functions and invokes each one.

Listing 3-14
Image from book

<script type="text/javascript">
function CreateFunction(num) {
    var NewFunction = function() {
        document.write(num + " squared = " + num*num + "<br />");
    }
    return NewFunction;
}

var functions = new Array();

for(var i = 0; i < 10; i++) {
    functions[i] = CreateFunction(i);
}

for(var j = 0; j < 10; j++) {
    functions[j]();
}
</script>
Image from book

When the functions are created, they carry with them the scope that existed at that time. It doesn’t matter that the variable used when creating the function has subsequently changed or even gone out of scope. The function reflects the closure of the scope in effect at the time it was created. The num variable no longer exists once the function-creating method returns, but the state of the transient variable is captured and used when the new function is called. Figure 3-3 shows the output of calling the functions to output their values.

Image from book
Figure 3-3

In the previous example, the scope of the variable being used was limited to the function that was creating new functions. The variable is declared as a function argument and is no longer available after the function returns. However, the closure that is created captures the state of that variable in that scope, and the function created is able to use it later when it is invoked. In Listing 3-15 (from ClosureScope.htm), I modify the example slightly to show how the scope is captured in a closure. A variable in the global scope is referenced from within the function being created. The variable is declared before the loop that is creating functions, and the variable is incremented during each pass. When the created functions are called, the scope that existed at the time of creation is used, not the value in play at that time. Although the value for the variable was different while each closure was created, just as in the previous example, the scope was different and thus the function is different.

Listing 3-15
Image from book
<script type="text/javascript">
function CreateFunction(num) {
 var NewFunction = function() {
  document.write( num + " squared = " + num*num + 
                  " and globalNum= " + globalNum + "<br />");
 }
 return NewFunction;
}

var globalNum = 0;
var functions = new Array();

for(var i = 0; i < 10; i++) {
 globalNum = globalNum + 2;
 functions[i] = CreateFunction(i);
}

for(var j = 0; j < 10; j++) {
 functions[j]();
}
</script>
Image from book

Every function that was created shows the current value of the global variable based on the captured scope. The variable was global when the closure was created, and the value at the time is immaterial. The scope is captured, and the value of the variable when the closure function is actually called is used. Since it is in the global scope, the current value of the variable is still in effect when the individual functions stored in the collection of functions are called. They all show the same value for the global variable as it is now, despite that the value was different when each function was created, as you can see in Figure 3-4.

Image from book
Figure 3-4

Garbage Collection

JavaScript is a garbage-collected language. When an object goes out of scope and there are no longer references to it, the system can free it and reclaim the memory and resources. This is one of the language features of C# and VB.NET that make them easy to use. C++ programmers are used to painstakingly allocating and freeing memory and objects by hand. Managing your own memory can be tedious, and mistakes can be devastating. JavaScript tracks which variables refer to what, and when there are no lingering references to an object, it is freed.

DOM Elements

Ultimately, what makes JavaScript useful in the browser is that it is able to access the DOM (Document Object Model). You can attach JavaScript functions to browser events and dynamically modify the HTML elements, CSS, and behavior of the page. Thus, JavaScript can take static pages and make them rich and interactive. In Listing 3-2, code is attached to the onLoad event of the browser’s window element.

As browsers evolved, they developed different Document Object Models. The World Wide Web consortium established a standard meant to provide isolation from the differences. In reality, there are still some challenges in writing code that will work on all of the major browsers, but the Microsoft AJAX Library provides some abstractions that make it easier to write code compatible with many popular browsers.

Avoiding Memory Leaks

Closures, garbage collection, and access to the DOM come together in a way that can manifest itself as a memory leak in the browser. It is easy to end up with circular references that involve a DOM element. The garbage collector in Internet Explorer sees this as a persistent reference and fails to recognize that the object can be freed. Even when a new page is loaded, the resource is still held, although the element does not exist anymore. Over time, the amount of memory consumed by the browser will creep up until the process is restarted.

A common way to create circular references is to attach a JavaScript function to the event of an HTML element. If the event handler code then references a property of the element, you have a circular reference. The Microsoft AJAX Library provides constructs for binding events that also clean up the circular references.

The process many developers use to avoid memory leaks is to attach to the unload event of the window. This event will fire when the user is through with the page and moving on. In the event handler, the user will loop through DOM and set the event handlers to null to break the circular references. Listing 3-16 (from circular.htm) is an example of attaching an event handler to a DOM element that creates a circular reference by accessing the source element.

Listing 3-16
Image from book

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Circular Reference</title>
<script type="text/javascript">
function eventHandler() {
    var tb = document.getElementById("tb");
    if(!!tb) {
        tb.value = "something else";
    }
}    
</script>
</head>
<body>
<form action="HTMLPage.htm">
<input type="text" id="tb" onclick="eventHandler()" />
</form>
</body>
</html>
Image from book