Chapter 7

Function Expressions

WHAT’S IN THIS CHAPTER?

  • Function expression characteristics
  • Recursion with functions
  • Private variables using closures

One of the more powerful, and often confusing, parts of JavaScript is function expressions. As mentioned in Chapter 5, there are two ways to define a function: by function declaration and by function expression. The first, function declaration, has the following form:

function functionName(arg0, arg1, arg2) {
    //function body
}

The name of the function follows the function keyword, and this is how the function’s name is assigned. Firefox, Safari, Chrome, and Opera all feature a nonstandard name property on functions exposing the assigned name. This value is always equivalent to the identifier that immediately follows the function keyword:

image
//works only in Firefox, Safari, Chrome, and Opera
alert(functionName.name);    //"functionName"

FunctionNameExample01.htm

One of the key characteristics of function declarations is function declaration hoisting, whereby function declarations are read before the code executes. That means a function declaration may appear after code that calls it and still work:

image
sayHi();
function sayHi(){
    alert("Hi!");
}

FunctionDeclarationHoisting01.htm

This example doesn’t throw an error because the function declaration is read first before the code begins to execute.

The second way to create a function is by using a function expression. Function expressions have several forms. The most common is as follows:

var functionName = function(arg0, arg1, arg2){
    //function body
};

This pattern of function expression looks like a normal variable assignment. A function is created and assigned to the variable functionName. The created function is considered to be an anonymous function, because it has no identifier after the function keyword. (Anonymous functions are also sometimes called lambda functions.) This means the name property is the empty string.

Function expressions act like other expressions and, therefore, must be assigned before usage. The following causes an error:

sayHi();    //error - function doesn't exist yet
var sayHi = function(){
    alert("Hi!");
};

Understanding function hoisting is key to understanding the differences between function declarations and function expressions. For instance, the result of the following code may be surprising:

//never do this!
if(condition){
    function sayHi(){
        alert("Hi!");
    }
} else {
    function sayHi(){
        alert("Yo!");
    }
}

FunctionDeclarationsErrorExample01.htm

The code seems to indicate that if condition is true, use one definition for sayHi(); otherwise, use a different definition. In fact, this is not valid syntax in ECMAScript, so JavaScript engines try to error correct into an appropriate state. The problem is that browsers don’t consistently error correct in this case. Most browsers return the second declaration regardless of condition; Firefox returns the first when condition is true. This pattern is dangerous and should not be used. It is perfectly fine, however, to use function expressions in this way:

//this is okay
var sayHi;
 
if(condition){
    sayHi = function(){
        alert("Hi!");
    };
} else {
    sayHi = function(){
        alert("Yo!");
    };
}

This example behaves the way you would expect, assigning the correct function expression to the variable sayHi based on condition.

The ability to create functions for assignment to variables also allows you to return functions as the value of other functions. Recall the following createComparisonFunction() example from Chapter 5:

function createComparisonFunction(propertyName) {
                   
    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
                   
        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}

createComparisonFunction() returns an anonymous function. The returned function will, presumably, be either assigned to a variable or otherwise called, but within createComparisonFunction() it is anonymous. Any time a function is being used as a value, it is a function expression. However, these are not the only uses for function expressions, as the rest of this chapter will show.

RECURSION

A recursive function typically is formed when a function calls itself by name, as in the following example:

image
function factorial(num){
    if (num <= 1){
        return 1;
    } else {
        return num * factorial(num-1);
    }
}

RecursionExample01.htm

This is the classic recursive factorial function. Although this works initially, it’s possible to prevent it from functioning by running the following code immediately after it:

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));  //error!

RecursionExample01.htm

Here, the factorial() function is stored in a variable called anotherFactorial. The factorial variable is then set to null, so only one reference to the original function remains. When anotherFactorial() is called, it will cause an error, because it will try to execute factorial(), which is no longer a function. Using arguments.callee can alleviate this problem.

Recall that arguments.callee is a pointer to the function being executed and, as such, can be used to call the function recursively, as shown here:

function factorial(num){
    if (num <= 1){
        return 1;
    } else {
        return num * arguments.callee(num-1);
    }
}

RecursionExample02.htm

Changing the highlighted line to use arguments.callee instead of the function name ensures that this function will work regardless of how it is accessed. It’s advisable to always use arguments.callee of the function name whenever you’re writing recursive functions.

The value of arguments.callee is not accessible to a script running in strict mode and will cause an error when attempts are made to read it. Instead, you can use named function expressions to achieve the same result. For example:

var factorial = (function f(num){
    if (num <= 1){
        return 1;
    } else {
        return num * f(num-1);
    }
});

In this code, a named function expression f() is created and assigned to the variable factorial. The name f remains the same even if the function is assigned to another variable, so the recursive call will always execute correctly. This pattern works in both nonstrict mode and strict mode.

CLOSURES

The terms anonymous functions and closures are often incorrectly used interchangeably. Closures are functions that have access to variables from another function’s scope. This is often accomplished by creating a function inside a function, as in the following highlighted lines from the previous createComparisonFunction() example:

function createComparisonFunction(propertyName) {
                   
    return function(object1, object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
                   
        if (value1 < value2){
            return -1;
        } else if (value1 > value2){
            return 1;
        } else {
            return 0;
        }
    };
}

The highlighted lines in this example are part of the inner function (an anonymous function) that is accessing a variable (propertyName) from the outer function. Even after the inner function has been returned and is being used elsewhere, it has access to that variable. This occurs because the inner function’s scope chain includes the scope of createComparisonFunction(). To understand why this is possible, consider what happens when a function is first called.

Chapter 4 introduced the concept of a scope chain. The details of how scope chains are created and used are important for a good understanding of closures. When a function is called, an execution context is created, and its scope chain is created. The activation object for the function is initialized with values for arguments and any named arguments. The outer function’s activation object is the second object in the scope chain. This process continues for all containing functions until the scope chain terminates with the global execution context.

As the function executes, variables are looked up in the scope chain for the reading and writing of values. Consider the following:

function compare(value1, value2){
    if (value1 < value2){
        return -1;
    } else if (value1 > value2){
        return 1;
    } else {
        return 0;
    }
}
                   
var result = compare(5, 10);

This code defines a function named compare() that is called in the global execution context. When compare() is called for the first time, a new activation object is created that contains arguments, value1, and value2. The global execution context’s variable object is next in the compare() execution context’s scope chain, which contains this, result, and compare. Figure 7-1 illustrates this relationship.

Behind the scenes, an object represents the variables in each execution context. The global context’s variable object always exists, whereas local context variable objects, such as the one for compare(), exist only while the function is being executed. When compare() is defined, its scope chain is created, preloaded with the global variable object, and saved to the internal [[Scope]] property. When the function is called, an execution context is created and its scope chain is built up by copying the objects in the function’s [[Scope]] property. After that, an activation object (which also acts as a variable object) is created and pushed to the front of the context’s scope chain. In this example, that means the compare() function’s execution context has two variable objects in its scope chain: the local activation object and the global variable object. Note that the scope chain is essentially a list of pointers to variable objects and does not physically contain the objects.

Whenever a variable is accessed inside a function, the scope chain is searched for a variable with the given name. Once the function has completed, the local activation object is destroyed, leaving only the global scope in memory. Closures, however, behave differently.

A function that is defined inside another function adds the containing function’s activation object into its scope chain. So, in createComparisonFunction(), the anonymous function’s scope chain actually contains a reference to the activation object for createComparisonFunction(). Figure 7-2 illustrates this relationship when the following code is executed:

var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });

When the anonymous function is returned from createComparisonFunction(), its scope chain has been initialized to contain the activation object from createComparisonFunction() and the global variable object. This gives the anonymous function access to all of the variables from createComparisonFunction(). Another interesting side effect is that the activation object from createComparisonFunction() cannot be destroyed once the function finishes executing, because a reference still exists in the anonymous function’s scope chain. After createComparisonFunction() completes, the scope chain for its execution context is destroyed, but its activation object will remain in memory until the anonymous function is destroyed, as in the following:

//create function
var compareNames = createComparisonFunction("name");
                   
//call function
var result = compareNames({ name: "Nicholas" }, { name: "Greg"});
                   
//dereference function - memory can now be reclaimed
compareNames = null;

Here, the comparison function is created and stored in the variable compareNames. Setting compareNames equal to null dereferences the function and allows the garbage collection routine to clean it up. The scope chain will then be destroyed and, all of the scopes (except the global scope) can be destroyed safely. Figure 7-2 shows the scope-chain relationships that occur when compareNames() is called in this example.

image

Since closures carry with them the containing function’s scope, they take up more memory than other functions. Overuse of closures can lead to excess memory consumption, so it’s recommended you use them only when absolutely necessary. Optimizing JavaScript engines, such as V8, make attempts to reclaim memory that is trapped because of closures, but it’s still recommended to be careful when using closures.

Closures and Variables

There is one notable side effect of this scope-chain configuration. The closure always gets the last value of any variable from the containing function. Remember that the closure stores a reference to the entire variable object, not just to a particular variable. This issue is illustrated clearly in the following example:

image
function createFunctions(){
    var result = new Array();
    
    for (var i=0; i < 10; i++){
        result[i] = function(){
            return i;
        };
    }
    
    return result;
}

ClosureExample01.htm

This function returns an array of functions. It seems that each function should just return the value of its index, so the function in position 0 returns 0, the function in position 1 returns 1, and so on. In reality, every function returns 10. Since each function has the createFunctions() activation object in its scope chain, they are all referring to the same variable, i. When createFunctions() finishes running, the value of i is 10, and since every function references the same variable object in which i exists, the value of i inside each function is 10. You can, however, force the closures to act appropriately by creating another anonymous function, as follows:

function createFunctions(){
    var result = new Array();
    
    for (var i=0; i < 10; i++){
        result[i] = function(num){
            return function(){
                return num;
            };
        }(i);
    }
    
    return result;
}

ClosureExample02.htm

With this version of createFunctions(), each function returns a different number. Instead of assigning a closure directly into the array, an anonymous function is defined and called immediately. The anonymous function has one argument, num, which is the number that the result function should return. The variable i is passed in as an argument to the anonymous function. Since function arguments are passed by value, the current value of i is copied into the argument num. Inside the anonymous function, a closure that accesses num is created and returned. Now each function in the result array has its own copy of num and thus can return separate numbers.

The this Object

Using the this object inside closures introduces some complex behaviors. The this object is bound at runtime based on the context in which a function is executed: when used inside global functions, this is equal to window in nonstrict mode and undefined in strict mode, whereas this is equal to the object when called as an object method. Anonymous functions are not bound to an object in this context, meaning the this object points to window unless executing in strict mode (where this is undefined). Because of the way closures are written, however, this fact is not always obvious. Consider the following:

image
var name = "The Window";
                   
var object = {
    name : "My Object",
                   
    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};
                   
alert(object.getNameFunc()());  //"The Window" (in non-strict mode)

ThisObjectExample01.htm

Here, a global variable called name is created along with an object that also contains a property called name. The object contains a method, getNameFunc(), that returns an anonymous function, which returns this.name. Since getNameFunc() returns a function, calling object.getNameFunc()() immediately calls the function that is returned, which returns a string. In this case, however, it returns "The Window", which is the value of the global name variable. Why didn’t the anonymous function pick up the containing scope’s this object?

Remember that each function automatically gets two special variables as soon as the function is called: this and arguments. An inner function can never access these variables directly from an outer function. It is possible to allow a closure access to a different this object by storing it in another variable that the closure can access, as in this example:

image
var name = "The Window";
                   
var object = {
    name : "My Object",
                   
    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};
                   
alert(object.getNameFunc()());  //"My Object"

ThisObjectExample02.htm

The two highlighted lines show the difference between this example and the previous one. Before defining the anonymous function, a variable named that is assigned equal to the this object. When the closure is defined, it has access to that, since it is a uniquely named variable in the containing function. Even after the function is returned, that is still bound to object, so calling object.getNameFunc()() returns "My Object".

image

Both this and arguments behave in this way. If you want access to a containing scope’s arguments object, you’ll need to save a reference into another variable that the closure can access.

There are a few special cases where the value of this may not end up as the value you expect. Consider the following modification to the previous example:

var name = "The Window";
 
var object = {
    name : "My Object",
 
    getName: function(){
        return this.name;
    }
};

The getName() method simply returns the value of this.name. Here are various ways to call object.getName() and the results:

object.getName();     //"My Object"
(object.getName)();   //"My Object"
(object.getName = object.getName)();   //"The Window" in non-strict mode

ThisObjectExample03.htm

The first line calls object.getName() in the way you normally would and so returns "My Object", as this.name is the same as object.name. The second line places parentheses around object.getName before calling it. While this might seem to be a reference just to the function, the this value is maintained, because object.getName and (object.getName) are defined to be equivalent. The third line performs an assignment and then calls the result. Because the value of this assignment expression is the function itself, the this value is not maintained, and so "The Window" is returned.

It’s unlikely that you’ll intentionally use the patterns in lines two or three, but it is helpful to know that the value of this can change in unexpected ways when syntax is changed slightly.

Memory Leaks

The way closures work causes particular problems in Internet Explorer prior to version 9 because of the different garbage-collection routines used for JScript objects versus COM objects (discussed in Chapter 4). Storing a scope in which an HTML element is stored effectively ensures that the element cannot be destroyed. Consider the following:

function assignHandler(){
    var element = document.getElementById("someElement");
    element.onclick = function(){
        alert(element.id);
    };
}

This code creates a closure as an event handler on element, which in turn creates a circular reference (events are discussed in Chapter 13). The anonymous function keeps a reference to the assignHandler() function’s activation object, which prevents the reference count for element from being decremented. As long as the anonymous function exists, the reference count for element will be at least 1, which means the memory will never be reclaimed. This situation can be remedied by changing the code slightly, as shown here:

function assignHandler(){
    var element = document.getElementById("someElement");
    var id = element.id;
                   
    element.onclick = function(){
        alert(id);
    };
                   
    element = null;
}

In this version of the code, a copy of element’s ID is stored in a variable that is used in the closure, eliminating the circular reference. That step alone is not enough, however, to prevent the memory problem. Remember: the closure has a reference to the containing function’s entire activation object, which contains element. Even if the closure doesn’t reference element directly, a reference is still stored in the containing function’s activation object. It is necessary, therefore, to set the element variable equal to null. This dereferences the COM object and decrements its reference count, ensuring that the memory can be reclaimed when appropriate.

MIMICKING BLOCK SCOPE

As mentioned previously, JavaScript has no concept of block-level scoping, meaning variables defined inside of block statements are actually created in the containing function, not within the statement. Consider the following:

image
function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
                   
    alert(i);   //count
}

BlockScopeExample01.htm

In this function, a for loop is defined and the variable i is initialized to be equal to 0. For languages such as Java and C++, the variable i would be defined only in the block statement representing the for loop, so the variable would be destroyed as soon as the loop completed. However, in JavaScript the variable i is defined as part of the outputNumbers() activation object, meaning it is accessible inside the function from that point on. Even the following errant redeclaration of the variable won’t wipe out its value:

function outputNumbers(count){
    for (var i=0; i < count; i++){
        alert(i);
    }
                   
    var i;  //variable redeclared
    alert(i);   //count
}

BlockScopeExample02.htm

JavaScript will never tell you if you’ve declared the same variable more than once; it simply ignores all subsequent declarations (though it will honor initializations). Anonymous functions can be used to mimic block scoping and avoid such problems.

The basic syntax of an anonymous function used as a block scope (often called a private scope) is as follows:

(function(){
    //block code here
})();

This syntax defines an anonymous function that is called immediately and is also sometimes called an immediately invoked function. What looks like a function declaration is enclosed in parentheses to indicate that it’s actually a function expression. This function is then called via the second set of parentheses at the end. If this syntax is confusing, consider the following example:

var count = 5;
outputNumbers(count);

In this example, a variable count is initialized with the value of 5. Of course, the variable is unnecessary since the value is being passed directly into a function. To make the code more concise, the value 5 can replace the variable count when calling the function as follows:

outputNumbers(5);

This works the same as the previous example because a variable is just a representation of another value, so the variable can be replaced with the actual value, and the code works fine. Now consider the following:

var someFunction = function(){
    //block code here
};
someFunction();

In this example, a function is defined and then called immediately. An anonymous function is created and assigned to the variable someFunction. The function is then called by placing parentheses after the function name, becoming someFunction(). Remember in the previous example that the variable count could be replaced with its actual value; the same thing can be done here. However, the following won’t work:

function(){
    //block code here
}();   //error!

This code causes a syntax error, because JavaScript sees the function keyword as the beginning of a function declaration, and function declarations cannot be followed by parentheses. Function expressions, however, can be followed by parentheses. To turn the function declaration into a function expression, you need only surround it with parentheses like this:

(function(){
    //block code here
})();

These private scopes can be used anywhere variables are needed temporarily, as in this example:

image
function outputNumbers(count){
    (function () {
        for (var i=0; i < count; i++){
            alert(i);
        }
    })();
    
    alert(i);   //causes an error
}

BlockScopeExample03.htm

In this rewritten version of the outputNumbers() function, a private scope is inserted around the for loop. Any variables defined within the anonymous function are destroyed as soon as it completes execution, so the variable i is used in the loop and then destroyed. The count variable is accessible inside the private scope because the anonymous function is a closure, with full access to the containing scope’s variables.

This technique is often used in the global scope outside of functions to limit the number of variables and functions added to the global scope. Typically you want to avoid adding variables and functions to the global scope, especially in large applications with multiple developers, to avoid naming collisions. Private scopes allow every developer to use his or her own variables without worrying about polluting the global scope. Consider this example:

(function(){
                   
    var now = new Date();
    if (now.getMonth() == 0 && now.getDate() == 1){
        alert("Happy new year!");
    }
                   
})();

Placing this code in the global scope provides functionality for determining if the day is January 1 and, if so, displaying a message to the user. The variable now becomes a variable that is local to the anonymous function instead of being created in the global scope.

image

This pattern limits the closure memory problem, because there is no reference to the anonymous function. Therefore the scope chain can be destroyed immediately after the function has completed.

PRIVATE VARIABLES

Strictly speaking, JavaScript has no concept of private members; all object properties are public. There is, however, a concept of private variables. Any variable defined inside a function is considered private since it is inaccessible outside that function. This includes function arguments, local variables, and functions defined inside other functions. Consider the following:

function add(num1, num2){
    var sum = num1 + num2;
    return sum;
}  

In this function, there are three private variables: num1, num2, and sum. These variables are accessible inside the function but can’t be accessed outside it. If a closure were to be created inside this function, it would have access to these variables through its scope chain. Using this knowledge, you can create public methods that have access to private variables.

A privileged method is a public method that has access to private variables and/or private functions. There are two ways to create privileged methods on objects. The first is to do so inside a constructor, as in this example:

function MyObject(){
                   
    //private variables and functions
    var privateVariable = 10;
                   
    function privateFunction(){
        return false;
    }
                   
    //privileged methods
    this.publicMethod = function (){
        privateVariable++;
        return privateFunction();
    };
}

This pattern defines all private variables and functions inside the constructor. Then privileged methods can be created to access those private members. This works because the privileged methods, when defined in the constructor, become closures with full access to all variables and functions defined inside the constructor’s scope. In this example, the variable privateVariable and the function privateFunction() are accessed only by publicMethod(). Once an instance of MyObject is created, there is no way to access privateVariable and privateFunction() directly; you can do so only by way of publicMethod().

You can define private and privileged members to hide data that should not be changed directly, as in this example:

image
function Person(name){
                   
    this.getName = function(){
        return name;
    };
                   
    this.setName = function (value) {
        name = value;
    };
}
                   
var person = new Person("Nicholas");
alert(person.getName());   //"Nicholas"
person.setName("Greg");
alert(person.getName());   //"Greg"

PrivilegedMethodExample01.htm

The constructor in this code defines two privileged methods: getName() and setName(). Each method is accessible outside the constructor and accesses the private name variable. Outside the Person constructor, there is no way to access name. Since both methods are defined inside the constructor, they are closures and have access to name through the scope chain. The private variable name is unique to each instance of Person since the methods are being re-created each time the constructor is called. One downside, however, is that you must use the constructor pattern to accomplish this result. As discussed in Chapter 6, the constructor pattern is flawed in that new methods are created for each instance. Using static private variables to achieve privileged methods avoids this problem.

Static Private Variables

Privileged methods can also be created by using a private scope to define the private variables or functions. The pattern is as follows:

(function(){
                   
    //private variables and functions
    var privateVariable = 10;
                   
    function privateFunction(){
        return false;
    }
                   
    //constructor
    MyObject = function(){
    };
                   
    //public and privileged methods
    MyObject.prototype.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
                   
})();

In this pattern, a private scope is created to enclose the constructor and its methods. The private variables and functions are defined first, followed by the constructor and the public methods. Public methods are defined on the prototype, as in the typical prototype pattern. Note that this pattern defines the constructor not by using a function declaration but instead by using a function expression. Function declarations always create local functions, which is undesirable in this case. For this same reason, the var keyword is not used with MyObject. Remember: initializing an undeclared variable always creates a global variable, so MyObject becomes global and available outside the private scope. Also keep in mind that assigning to an undeclared variable in strict mode causes an error.

The main difference between this pattern and the previous one is that private variables and functions are shared among instances. Since the privileged method is defined on the prototype, all instances use that same function. The privileged method, being a closure, always holds a reference to the containing scope. Consider the following:

image
(function(){
                   
    var name = "";
    
    Person = function(value){                
        name = value;                
    };
    
    Person.prototype.getName = function(){
        return name;
    };
    
    Person.prototype.setName = function (value){
        name = value;
    };
})();
                   
var person1 = new Person("Nicholas");
alert(person1.getName());   //"Nicholas"
person1.setName("Greg");
alert(person1.getName());   //"Greg"
                   
var person2 = new Person("Michael");
alert(person1.getName());   //"Michael"
alert(person2.getName());   //"Michael"

PrivilegedMethodExample02.htm

The Person constructor in this example has access to the private variable name, as do the getName() and setName() methods. Using this pattern, the name variable becomes static and will be used among all instances. This means calling setName() on one instance affects all other instances. Calling setName() or creating a new Person instance sets the name variable to a new value. This causes all instances to return the same value.

Creating static private variables in this way allows for better code reuse through prototypes, although each instance doesn’t have its own private variable. Ultimately, the decision to use instance or static private variables needs to be based on your individual requirements.

image

The farther up the scope chain a variable lookup is, the slower the lookup becomes because of the use of closures and private variables.

The Module Pattern

The previous patterns create private variables and privileged methods for custom types. The module pattern, as described by Douglas Crockford, does the same for singletons. Singletons are objects of which there will only ever be one instance. Traditionally, singletons are created in JavaScript using object literal notation, as shown in the following example:

var singleton = {
    name : value,
    method : function () {
        //method code here
    }
};

The module pattern augments the basic singleton to allow for private variables and privileged methods, taking the following format:

var singleton = function(){
                   
    //private variables and functions
    var privateVariable = 10;
                   
    function privateFunction(){
        return false;
    }
                   
    //privileged/public methods and properties
    return {
                   
        publicProperty: true,
                   
        publicMethod : function(){
            privateVariable++;
            return privateFunction();
        }
                   
    };
}();

The module pattern uses an anonymous function that returns an object. Inside of the anonymous function, the private variables and functions are defined first. After that, an object literal is returned as the function value. That object literal contains only properties and methods that should be public. Since the object is defined inside the anonymous function, all of the public methods have access to the private variables and functions. Essentially, the object literal defines the public interface for the singleton. This can be useful when the singleton requires some sort of initialization and access to private variables, as in this example:

image
var application = function(){
                   
    //private variables and functions
    var components = new Array();
                   
    //initialization
    components.push(new BaseComponent());
                   
    //public interface
    return {
        getComponentCount : function(){
            return components.length;
        },
                   
        registerComponent : function(component){
            if (typeof component == "object"){
                components.push(component);
            }
        }
    };
}();

ModulePatternExample01.htm

In web applications, it’s quite common to have a singleton that manages application-level information. This simple example creates an application object that manages components. When the object is first created, the private components array is created and a new instance of BaseComponent is added to its list. (The code for BaseComponent is not important; it is used only to show initialization in the example.) The getComponentCount() and registerComponent() methods are privileged methods with access to the components array. The former simply returns the number of registered components, and the latter registers a new component.

The module pattern is useful for cases like this, when a single object must be created and initialized with some data and expose public methods that have access to private data. Every singleton created in this manner is an instance of Object, since ultimately an object literal represents it. This is inconsequential, because singletons are typically accessed globally instead of passed as arguments into a function, which negates the need to use the instanceof operator to determine the object type.

The Module-Augmentation Pattern

Another take on the module pattern calls for the augmentation of the object before returning it. This pattern is useful when the singleton object needs to be an instance of a particular type but must be augmented with additional properties and/or methods. Consider the following example:

var singleton = function(){
                   
    //private variables and functions
    var privateVariable = 10;
                   
    function privateFunction(){
        return false;
    }
                   
    //create object
    var object = new CustomType();
                   
    //add privileged/public properties and methods
    object.publicProperty = true;
                   
    object.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
                   
    //return the object
    return object;
}();

If the application object in the module pattern example had to be an instance of BaseComponent, the following code could be used:

image
var application = function(){
                   
    //private variables and functions
    var components = new Array();
                   
    //initialization
    components.push(new BaseComponent());
                   
    //create a local copy of application
    var app = new BaseComponent();
                   
    //public interface
    app.getComponentCount = function(){
        return components.length;
    };
                   
    app.registerComponent = function(component){
        if (typeof component == "object"){
            components.push(component);
        }
    };
                   
    //return it
    return app;
}();

ModuleAugmentationPatternExample01.htm

In this rewritten version of the application singleton, the private variables are defined first, as in the previous example. The main difference is the creation of a variable named app that is a new instance of BaseComponent. This is the local version of what will become the application object. Public methods are then added onto the app object to access the private variables. The last step is to return the app object, which assigns it to application.

SUMMARY

Function expressions are useful tools in JavaScript programming. They allow truly dynamic programming where functions need not be named. These anonymous functions, also called lambda functions, are a powerful way to use JavaScript functions. The following is a summary of function expressions:

  • Function expressions are different from function declarations. Function declarations require names, while function expressions do not. A function expression without a name is also called an anonymous function.
  • With no definitive way to reference a function, recursive functions become more complicated.
  • Recursive functions running in nonstrict mode may use arguments.callee to call themselves recursively instead of using the function name, which may change.

Closures are created when functions are defined inside other functions, allowing the closure access to all of the variables inside of the containing function, as follows:

  • Behind the scenes, the closure’s scope chain contains a variable object for itself, the containing function, and the global context.
  • Typically a function’s scope and all of its variables are destroyed when the function has finished executing.
  • When a closure is returned from that function, its scope remains in memory until the closure no longer exists.

Using closures, it’s possible to mimic block scoping in JavaScript, which doesn’t exist natively, as follows:

  • A function can be created and called immediately, executing the code within it but never leaving a reference to the function.
  • This results in all of the variables inside the function being destroyed unless they are specifically set to a variable in the containing scope.

Closures can also be used to create private variables in objects, as follows:

  • Even though JavaScript doesn’t have a formal concept of private object properties, closures can be used to implement public methods that have access to variables defined within the containing scope.
  • Public methods that have access to private variables are called privileged methods.
  • Privileged methods can be implemented on custom types using the constructor or prototype patterns and on singletons by using the module or module-augmentation patterns.

Function expressions and closures are extremely powerful in JavaScript and can be used to accomplish many things. Keep in mind that closures maintain extra scopes in memory, so overusing them may result in increased memory consumption.

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

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