If one looks at the ECMAScript code I've posted to this forum, one should notice a simple pattern, or at least variations of it:
Code:
var identifier = (function() {
/* ... */
})();
Code as simple as this will prevent the creation of unnecessary global variables, but provide something akin to private member variables available in other object-oriented languages (don't forget that ECMAScript is OO, just in a rather unique way). This operates on the basis that the function expression (shown above) acts like a wrapper to the code placed inside of it. Any variable created using the var keyword will become a local variable, usable by any code contained within the expression.
This functionality is provided by a relatively unknown feature of ECMAScript: the closure. When a function is called, it creates an execution context. This context holds various bits of state information for that function, primarily: the scope chain (used to resolve identifiers), the arguments passed to the function, and the local variables and functions defined within.
When an inner function (a function defined within another function) is created, this state information is added to its scope chain. This allows the inner function to reference the variables, functions, and formal (named) arguments defined by the outer function. If the inner function can survive (it would normally be destroyed when the outer function returns), a closure is formed. Consider:
Code:
function outerA() {
var localA;
function innerA() {
}
}
function outerB() {
var localB;
function innerB() {
}
return innerB;
}
The function, outerA, defines two local variables; localA which is undefined, and innerA1 which is a function. If outerA calls innerA, the inner function could access localA. However, once outerA returns, both of the local variables will be destroyed.
The function, outerB, is very similar. However, it returns a reference to its inner function, innerB. If you store this return value, the garbage collector cannot remove the inner function. Moreover, as the inner function has the execution context of outerB in its scope chain, localB and its value cannot be removed, either. This allows innerB to continue to reference and alter, if desired, the value of localB.
What is even more useful is the "uniqueness" factor. If outerB is called a second time, a totally new execution context is created as this slightly more complicated example shows:
Code:
var a, b;
/* Returns a function reference which, when
* called, can reference the argument, arg
* of the outer function.
*/
function outer(arg) {
return function() {
return arg;
};
}
a = outer(1);
b = outer(2);
a(); // returns 1
b(); // returns 2
Notice that the functions referenced by a and b maintain the initial value passed to outer; the second call doesn't overwrite it.
How exactly one uses this pattern depends precisely on what is desired. The snippet at the very beginning of this post would provide one-time-only execution of the code within the function expression. This can be useful for providing singleton objects or private static members[1]:
Code:
var singleton = (function() {
/* Private methods/variables */
return {
/* property : value pairs */
};
})();
var MyObject = (function() {
/* Private static methods/variables */
/* MyObject constructor */
function MyObject() {
}
return MyObject;
})();
Private instance members[2] are equally possible:
Code:
function MyObject() {
var privateMember = 5;
this.method1 = function() {return privateMember;};
}
MyObject.prototype.method2 = function() {return this.privateMember;};
In this example, method1 is said to be priviledged as it, unlike method2 has access to privateMember. Note that the return value of method2 would be undefined as there is no public member named privateMember.
There are countless possible applications of closures, including the ability to reconfigure objects or functions at run-time based on the host environment (extremely useful for Web scripting). A detailed discussion can be found in the comp.lang.javascript FAQ notes and topics similar to this post at Douglas Crockford's website.
Mike
Credits go to Richard Cornford, Douglas Crockford and Lasse Reichstein Nielsen for introducing this concept, and related material, to me in the first place - I'm merely passing it along. Thanks also go to Yann-Erwan Perio, and the above, for their help and inspiration since I first began participating in comp.lang.javascript.
[1] Public static members can be created by adding members directly to a constructor function:
Code:
function MyObject() {
}
MyObject.staticMethod = function() {
};
[2] Public instance members should be common knowledge. However there are two ways of creating them.
- Using the prototype object:
Code:
function MyObject() {
}
MyObject.prototype.method = function() {
/* ... */
};
var obj = new MyObject();
obj.method();
- Within the constructor:
Code:
function MyObject(param) {
this.method = function() {
/* ... */
};
}
var obj = new MyObject();
obj.method();
Although both snippets would have the same external functionality, they both serve two very different purposes. The latter example should be used if (and only if)
- A data member needs to be initialised at object construction-time based on something, most likely an argument to the constructor.
- A method needs to be priviledged. That is it needs to access private data.
Bookmarks