JavaScript doesn’t get enough respect. Sure it has warts but it also has elegant features like closures.
What, you may be asking, is a closure?
Many programming languages make a sharp distinction between code and data. JavaScript does not. In JavaScript a Function is a ‘first class’ type just like a String or an Array. Functions can be assigned to variables and properties and returned from other functions.
Declaring a function in JavaScript creates a function object. The following statement creates an object named example whose type, as reported by the typeof operator, is ‘function’.
function example(arg)
{
return "example: " + arg;
}
The example function could also be created by a function literal1:
var example = function(arg) { return "example: " + arg; };
What gives rise to closures is the way the scope chain is handled when a function is created.
Consider:
var test0;
function outer()
{
var test1;
function inner()
{
var test2;
}
}
JavaScript is a lexically scoped language. In the code above test0 is a global variable and test1 is local to the outer function. outer gets a reference to the global scope and test0 and test1 are both within scope for outer. test2 is local to the inner function. A function, in this case inner, always gets a reference to the scope chain in effect at the place where the function is defined. That is, inner gets a reference to outer’s local scope which in turn chains to the global scope. test0, test1, and test2 are all in scope for inner.
If the preceding description of scope makes sense, then consider this code:
function makeFish(color)
{
function fish()
{
return color + " fish";
}
return fish;
}
var fish0 = makeFish('red');
var fish1 = makeFish('blue');
var str = fish0() + '; ' + fish1();
The variable str will be assigned the value “red fish; blue fish”. Why? Because the scope chain persists with the function object. The function is said to be ‘closed’ over the scope in which it was defined.
This isn’t just a neat parlor trick. Closures, along with first class functions and recursion, are generally considered the essentials for a style of programming known as functional programming.
Effective JavaScript Closures
Defining a function in JavaScript always creates a closure. Closure creation can not be disabled or turned off.
If the function is in the global scope then only the global scope is captured in the closure. In general that’s not useful so it’s often ignored as a case of a closure. But be aware that implicitly defined variables, that is variables defined without a var statement, are always in the global scope.
JavaScript is not block scoped. C/C++, Java, and C# are all C style syntax languages and all are block scoped languages. JavaScript has a C style syntax but it is not block scoped.
It’s a common technique to leverage block scope to control the lifetime of variables. In JavaScript, variables are in scope throughout the function they are declared in, not just the block they are within. In function foo below the idx variable exists before and after the for statement’s block. In JavaScript a variable can’t be forced out of scope prior to exitting its function.
function foo()
{
var str = '';
for (var idx = 0; idx < 5; ++idx)
{
str += idx;
}
return str;
}
Closure Conniptions
You may have heard that JavaScript closures are evil and cause memory leaks. Nonsense. Closures are not evil.
Oh. The memory leak thing? Well, yeah. There’s an Internet Explorer memory leak issue2 that closures often get the brunt of the blame for. That’s unfortunate because the memory leak issue is not really a closure problem. It’s a host object integration issue.
JavaScript supports native objects and host objects. Native objects are the built-ins (e.g. Object, Function, Array, String, Number) and user defined objects. Host objects are provided by the hosting application.
For an example take the window object. The window object is not part of the JavaScript language. Yes I know your JavaScript book spends chapters on the window object. That’s because most JavaScript programmers are writing to web browsers and every web browser has a window object. But although every web browser provides a window object not every JavaScript host is a web browser.
With closures it can be very easy to create circular references between objects. (That’s bad.) But JavaScript’s garbage collector is capable of detecting and handling circular references. (That’s good.) Except that, in IE, COM based host objects aren’t managed by the JavaScript garbage collector. (That’s bad.) DOM objects in IE are COM host objects. (That’s more bad.)
The fix is easy. Don’t depend on the garbage collector. Clean up after yourself. Release and null out object references when you’re done with them.
Link: Scott's "SiteExperts" Place: Closures and IE Circular References.
Link: Fabulous Adventures In Coding : What are closures?.
Related Posts:
Using Properties and Tuples to Get Multiple Values from a JavaScript Function
1 In addition to function statements and function literals, a function can also be created by the Function constructor.
var example = new Function("arg", 'return "example: " + arg;');
The Function constructor behaves differently from statements and literals with respect to scope. It always creates the function in the global scope. That difference makes the function constructor generally non-useful for closures.
2 I would assume that the other JScript script engine hosts, WSH and ‘classic’ ASP, have the same issue.
This is a very nice introduction to closures, well thought-out and written. Well done!
Posted by: Jason | June 08, 2006 at 11:53 AM
Thanks, Jason. I always appreciate feedback.
Posted by: Jonathan Dodds | June 08, 2006 at 06:55 PM
That's interesting what you say about the garbage collectors. This affects my multi-threading JavaScript host as it uses COM to handle thread communication and I was hoping that COM's reference counts would be accurate so they could be used to drive my lower level reference counts.
http://www.kirit.com/Blog:/2007-11-30/JavaScript%20garbage%20collection
I think you probably already knew that functions are also a kind of object in JavaScript.
Posted by: Kirit | November 30, 2007 at 09:28 AM