3.6. Close on Closures

When discussing the prototype object, it is 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 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 the way you can define an inner function to take advantage of the state of the outer function's context and make it visible externally so it can execute 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 Visual Basic, with one notable exception (as shown in Figure 3-1 at the beginning of the chapter): 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 when you try to use transient state information in a prototype extension. The various instances of the object type being extended should not 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.

Scoping is much easier to see through an example. Listing 3-20 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.

Example 3-20. Scoping in JavaScript
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Scoping in JavaScript</title>

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

</head>
<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
</body>
</html>

When the functions are created, they carry with them the scope that existed at that time. It does not 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 invoked. Figure 3-12 shows the results of calling the functions to output their values.

Figure 3-12. Figure 3-12

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.

Listing 3-21 modifies 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, not the value in play at that time is used. 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.

Example 3-21. Scoping in JavaScript
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Scoping in JavaScript</title>

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

</head>
<body>
    <form id="form1" runat="server">
    <div>
    </div>
    </form>
</body>
</html>

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

Figure 3-13. Figure 3-13

3.6.1. 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 Visual Basic that makes 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.

3.6.2. DOM Elements

Ultimately, what makes JavaScript useful in the browser is that it is able to access the Document Object Model (DOM). 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. Earlier in Listing 3-6, code is attached to the onload() event of the browser's window element.

As browsers evolved, they developed different DOMs. 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.

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

To avoid memory leaks, many developers attach Javascript functions to the unload event of the window. This event fires when the user is through with the page and is 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-22 is an example of attaching an event handler to a DOM element that creates a circular reference by accessing the source element.

Example 3-22. Breaking circular references
<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 id="form1" runat="server">
    <div>
        <input type="text" id="tb" onclick="eventHandler()" />
    </div>
    </form>
</body>
</html>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset