A brief introduction to closures
In this post, I’m going to attempt to explain what closures are and how to use them. Many modern (and some not-so-modern) programming languages contain support for closures, but for the purposes of this article I’m going to be using JavaScript. I’ve chosen JavaScript for a few reasons:
- Ubiquity: If you have a web browser then you have a JavaScript interpreter
- Simplicity: JavaScript is conceptually a fairly simple language (especially if you limit yourself to its Good Parts), compared to other dynamic scripting languages such as Python and Ruby
- Familiarity: If you’ve used any of the C family of languages (e.g. C++, Java or C#) then JavaScript will look fairly familiar.
There may be some differences between languages with the mechanics, but deep down, closures are the same across all languages which allow them, so if you can understand the concept in JavaScript, you’ll understand it in any capable language.
Functional programming
Before I can get onto closures, I need to give a very brief introduction to functional programming.
Functional programming is all about functions (I doubt that comes as much of a surprise). In a language that surports functional programming, functions are generally first-class objects. That means they can be assigned to variables, stored in data structures, passed into functions and returned from functions.
In JavaScript, it’s important to distinguish between referencing a function and calling a function. To reference a function, just use the function name. To call a function, append parentheses (with optional arguments). Here’s an example:
function f() {
alert('f called!');
}
f(); // calls f
var x = f; // f is a reference to the function, and now x is too
x(); // calls f (or x - they're the same thing)
It’s also possible to create anonymous functions in JavaScript (aka lambdas). If you want to create an anonymous function, leave out the name:
function () {
alert('I have no name');
}
Another name for an anonymous function is a function literal. If you imagine it like a string literal you’ll see it’s an expression just like any other literal, and so it can be assigned to a variable:
var str = "string"; //string literal
var arr = [1, 2, 3]; //array literal
var obj = { 'JS': 1,
'is': 2,
'awesome': 3 }; //object literal
var f = function () {
alert("I've been assigned to f");
}; //function literal
Note that there is very little difference between assigning an anonymous
function to a variable called f
and defining a function called f
- the end
result of both is a function that can be referred to by the name f
, and can
be called with f()
.
Another capability of functional languages is the ability to nest functions. It’s perfectly valid in JavaScript to define one function within another. Here’s another example:
function outer() {
function inner() {
alert('In ur function');
}
inner(); // we can call inner here as it is defined in outer's scope
}
inner(); // Error - but not here - inner is not defined in this scope
It’s this ability, coupled with the first-class nature of functions, that enables closures.
A real-life closure
A closure is a function with access to variables in its containing scope (the function “closes over” the variables). The thing that can be tricky to wrap your head round is that the inner function still has access to the outer function’s variables after the outer function has returned. Here’s an example:
function outer() {
var counter = 0;
function inner() {
alert(counter);
counter++;
}
return inner;
}
var x = outer(); // As we're calling outer here, x is a reference to inner
x(); // alerts 0
x(); // alerts 1
x(); // alerts 2
In this code, outer
is called once, and returns inner
. x
is a reference
to inner
. Because inner
is a closure, it has access to outer
’s local
variable, counter
. Even though outer
has returned, inner
still has access
to outer
’s variables. Be careful though - if outer
were called again, we’d
get a new version of inner
. To continue the previous example:
var y = outer(); // Call outer again
y(); // alerts 0 - this is a different closure to the previous one.
The closure has access to any arguments in the containing scope as well:
function outer(x) {
function inner() {
alert(x);
}
return inner;
}
var func = outer(5);
func(); //alerts 5 - inner has access to the argument
A more advanced example
You should now know enough about the basics of closures to understand a more complex example. Here’s a way to give a JavaScript object private variables:
function CatMaker(name) {
var age = 10;
//construct an object on the fly with three methods.
//All methods have access to age, but age cannot be
//directly accessed outside of this function.
return {
"sayHello": function () { //first method
alert("Miaow");
},
"getAge": function (inCatYears) { //second method
if (inCatYears) {
return age * 7;
}
else {
return age;
}
},
"makeOlder": function () { //third method
age++;
}
};
}
var mycat = CatMaker('Snuffles');
mycat.getAge(true); //returns 70
mycat.makeOlder();
mycat.getAge(true); //returns 77
The ONLY way to make changes to the private variable age
is through the
method makeOlder
. All the methods share the same age
variable, because they
were all made in the same call of CatMaker
. If we called CatMaker
again to
produce a new cat, it would have its own age
variable.
The infamous lambda-in-a-loop problem
Consider the following example:
function attachListeners() {
for (var i = 0; i < 10; i++) {
$('#id-' + i).click(function () {
alert("I am element number " + i);
});
}
}
It’s using jQuery. To understand this example you’ll need to know a tiny amount
of how jQuery works. jQuery creates a global function called jQuery
, aliased
to $
. The jQuery function takes (among other things) a CSS selector. It
returns a jQuery object representing the HTML elements matching that selector.
A click handler can be added by calling .click()
on the returned object.
.click()
takes a function to be executed when the matched elements are
clicked on. $('#myid').click(function () { alert('hello'); });
will show an
alert when the HTML element with an id of ‘myid’ is clicked on.
The example selects 10 elements on the page, with the ids id-0
,
id-2
up to id-9
. Each time round the loop, a new click handler is created.
When you click on these elements, an alert box comes up. Some would think that
each click handler has its own version of i
but you know better. All the
click handlers share the same i
. Because of the way the for
loop works,
this value is one past the end of the loop, i.e. 10. So each element proudly
announces that it is element number 10, which is clearly incorrect.
The problem here is that although a new closure is being produced each time round the loop, each closure shares the same environment, so i
in each closure is the same i
as in all the others.
Take a look at a working demonstration of the above code using jsFiddle. Have a play with it so you can get a feel for what’s going on.
The only way around this is to use another function:
function addOneListener(i) { //Each time i is bound to a different value
$('#id-' + i).click(function () {
alert("I am element number " + i);
});
}
function addEventListeners() {
for (var i = 0; i < 10; i++) {
addOneListener(i);
}
}
Here’s the above as a fiddle. emehrkay in the comments has an alternative implementation of the above using raw DOM methods - it’s often useful to see different ways of achieving the same thing.
The key to understanding the new example is that every time addOneListener
is
called, a new closure is produced, and each of these closures has a different
i
. When you start using closures in JavaScript, this will bite you
eventually, so beware. It’s such a common issue that bobince of
HTML-parsing regex fame
put it at number three in his
list of common questions on Stack Overflow:
At number three it’s a new entry for “Why is my (Python, JavaScript, …) function getting the same value of the variable every time around the loop?”, by Clint Forloop and the Closures