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.
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.
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.
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.
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.
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.
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.