Wednesday, March 21, 2007

Closures in Javascript

I've grown up on C, and then a bit more on C++. I used to write a lot of networking and systems code, and then application level code to build backend distributed systems for an online retailer. Having dabbled a little bit in Java, and even less in Lisp, I've adopted Ruby as the language I mostly code in these days. But all these languages were a charm to get used to and develop in when I compare them with Javascript.

Why? Several reasons, but primarily because so much of the Javascript is about the browser, and while how you do something depends entirely on which broiwser your code is going to run on, there is hardly any good development and testing environment. To top that, I've not had the pleasure of reading a really good reference-like book for Javascript. If you know one, do tell me about it.

With the lack of books, one tends to rely on Google search to throw up some useful results. But some things are what you have to go through to pick up anyway. That brings me to closures. Though people have written about closures, they tend to only cover one aspect of lexical binding, and I wanted to add to this pool of collective online intelligence.

Closures let you define functions on the fly that are bound to the local lexical scope. Using these closures you can execute code that will use and affect variable references as available in the lexical scope of where the function is defined. Here's an example:


function f1() {

var letter = "A";
return function() { alert(letter); }

}

function f2() {
var letter = "B";
var f = f1();
f();
}

f2();


Run it here
.

The function f1 above returns a closure. The function returned is bound to the lexical scope of function f1. Once f1 is done, one would think that the all local variables etc are removed, and calling the function that is returned back by f1 should really use "B" as the value of the letter. Or maybe you would expect it to raise an error, because the scope of function f2 doesn't exist anymore. But if you run it, you'll still find that the function f returned by f1() call alerts with "A."

Once you see this, it is somewhat obvious what is happening. Except one thing, and we'll come to that shortly. What seems to be happening is that when the function in f2 is created (or rather the closure), it keeps track of its lexical scope. So all the references available in its scope are available to it. The reference that is returned is a reference to a set of code and its scope as was available then. That scope is what is used to execute the code in the closure.

What seems to be slightly less obvious is that the closure captures the references and not the values of these references in its scope. I.e., the closure does not copy all the values of the variables in its scope and keep them around for later. It keeps track of the references in its scope. If their values change by the time the closure is called, then the result would be different. Here is an update to the above example.


var letter = "A";
function f1() {
return function(callfrom) { alert( callfrom + " " + letter); }
}

function f2() {
var letter = "B";
var f = f1( );
f("call from f2");
}

function f3() {
letter = "C";
var f = f1( );
f( "call from f3" );
}

f2();
f3();



If you run the code above (here), you'll find that the call from f2 uses the value "A" for letter and the call from f3 uses the value "C."

The reason is again obvious once you see it, but it is easy to trip over this when using closures bound to variables in a loop. In the code above, f2 creates a new variable letter in its scope. This variable is not available in the lexical scope of the closure returned by f1. f1 uses the earlier defined letter. However, f3 does not define a new variable -- it updates the variable in global scope, the same variable that is also used by f1. The call from f3 to the closure evaluates the variable (the reference) 'letter' and it evaluates to "C" and not "A."

An important ramification of this is that when you are creating closures as callbacks inside a loop, and binding them to the variables that change with every iteration of the loop, you have to be extra careful. Unless you want to use these variables' values as they exist after the loop is done (or whenever the callback is called -- which might make it unpredictable), you have to create unique lexical references for each of these variables. A simple way to do this is to write a method that returns back the actual closure bound to its local variables. Every time this function is called, it generates a new scope for the closure. An example:




function myFunctor( x) {
return function() { alert( x); }
}

function testGoodFunctor() {

var functors = new Array;
for( var i = 0; i < 3; ++i ) {
functors.push( myFunctor(i) );
}
for( var j = 0; j < functors.length; ++j ) {
functors[j]();
}
}

function testBadFunctor() {

var functors = new Array;
for( var i = 0; i > 3; ++i ) {
functors.push( function() { alert(i); } );
}
for( var j = 0; j < functors.length; ++j ) {
functors[j]();
}
}

testGoodFunctor();
testBadFunctor();


If you run the code above (here), you will see three alerts with values 0, 1 and 2, followed by three alerts with value 3.

The reason, as you would have gathered by now, is that in the testGoodFunctor, each closure gets a unique reference for the alert call. However, the testBadFunctor provides the same reference "i" to the closure. When the closure is actually called, the value of i is the value it is supposed to have at the end of the loop.

There now, that's closures for you. And yes, there is one more thing to remember. Be careful not to use "this" to point to the object inside the closure (unless you know exactly what you are doing). "this" is not a locally defined reference, and when you closure is invoked, "this" may point to something completely different. If you want to invoke your methods on a specific object, it is better to create methods that are bound to the specific object. For example, like this:



function getObjectBoundMethod( obj, f ) {
return function() { return f.apply(obj,arguments); };
}

function f() {
this.g = function(x) { alert( "x = " + x ); };
var closure = getObjectBoundMethod( this, this.g );
return closure;
}

var to_call = f();
to_call(5);



Here, run it.

Update: Fixed the links to the running javascript samples.

4 comments:

Kode said...
This comment has been removed by the author.
Kode said...

welcome to the world of 'anonymous functions' 8 )

Keep Clicking,
Bhasker V Kode

PS : hey, your demos links dont seem to be valid btw .

Vikas said...

Bhaskar: Thanks for pointing out the links. I'll fix them shortly.

Vikas said...

The links should be working now.