Please join me at my new location bryankyle.com

Tuesday, February 12, 2008

Closures and Javascript

Ah Closures, one of the must misunderstood and accidentally used features of javascript. But why is this the case? Well, because it's so easy to accidentally use them few understand the mechanics behind them. In this post I hope I can clear up the confusion and explain how they work.

In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables.

Or, in normal people terms, a closure is a function that can access variables from the same scope that it was declared in. You can think of it as a natural extension of the standard scoping rules where functions declared in the global scope can access global variables.

var my_global = "hello, world";

function printGlobal() {
   print(my_global);
}

In the above code the function printGlobal can access a global variable. As an extension of the same principle, in the following code the function printLocal can access a local variable in its declaring scope.

function main() {
   var my_local = "hello, world";

   function printLocal() {
      print(my_local); 
   }
}

So that's all well and good, but why on earth would you ever want to use it? All your life you've been told that global variables are evil (which they are), so surely closures must also be evil. Well, if used incorrectly of course they're evil, but with great power comes great responsibility.

Here's quick example of a function that returns a function to increment a value by another fixed value.

function incr(start, delta) {
   return function() {
      start = start + delta;
      return  start;
   }
}

var i = incr(10, 1);
i(); // 11
i(); // 12
i(); // 13

Pretty freakin' wild isn't it? But how does that work? When a call is made to the incr function start and delta are bound to its scope. The function it returns simply holds onto these variables. Since it has a reference to the variables (not just their values, but a place to store them) they can be freely modified.

The next question you might be asking is, "So that's all well and good, but what if I call incr twice and hold on to both of the returned functions? Won't the second call clobber the first?". The answer is: NO! Each call to the method creates a new local scope just for that invocation. Exactly the same as how two calls to the same function at the same time won't interfere with each other.

But by far, the most classic use of closures is to enable Currying, and I'm not talking about the delicious spice blend. No no, I'm talking about higher-order functions. Functions that work with functions. Functions that return functions that call other functions. Functions that I'll explain in another post.