A blog about software engineering and other topics.written by Jonathan Dodds

May 17, 2006

JavaScript Closures

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

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:

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.

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.

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.

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.

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.