Yet Another Explanation of JavaScript Closures
26 April 2011 · Estimated reading time: 4 minutes
The closure is perhaps the most important and least understood concept in JavaScript. There are many excellent explanations already on the intertubes. To these, I humbly add mine.
I won’t pretend this is anything other than documentation of my current understanding of closures as restricted by my ability to communicate what is in my head. Still, I hope it contributes to rather than muddies the conversation.
My lay explanation is the following:
A closure is like a crystal ball that can only see into where it was created.
If you already understand closures, that probably makes sense. If not, don’t worry, I’m just setting up the mental model for you. Let’s get into the meat of it, starting with an example.
function outer() { var n = 10 function inner() { alert(n) } return inner } var enclosured = outer() enclosured() // alerts 10 alert(n) // throws a ReferenceError (see below)
What’s happening here? First, we define a function, outer
(for the record, “outer”, “inner”, and “enclosured” could be any valid function names) that contains a variable n
and a function inner
. Calling outer
returns a reference to inner
and assigns it to our variable, enclosured
. Calling enclosured
invokes the inner
function, alerting the value 10.
In this example, inner
is like the crystal ball and outer
is where it was created. Cool, eh?
To fully grok what’s going on under the hood, you need to understand the concept of scope. In JavaScript, scope is like a container for references to variables and function definitions. How closures work is that the current scope retains a reference to its enclosing scope dynamically and automagically. More on that below.
One of the nifty (and when you’re trying to debug stuff sometimes maddening) things about the language is that when a reference to a variable or function is missing, JavaScript will look “up the scope chain” for it. So, if something’s not in the current scope, JavaScript will look in the current scope’s container for it, in the container’s container, and so on.
If JavaScript climbs all the way up to the global scope and still doesn’t find the variable or function, then it will throw a ReferenceError (like when we tried to alert n
outside of our function above).
In the above example, inner
remembers that it was created inside outer
so when it goes looking for the variable n
and doesn’t find it in its own scope, it looks up the scope chain and finds it in the the enclosing (outer
’s) scope.
This is pretty powerful stuff. For example, it allows us to create private variables, store DOM references and access them later when the user clicks, etc.
JavaScript Dynamite!
JavaScript is what is known as a dynamic language. That means variables and even functions can be redefined, on-the-fly, at run time (or, as I like to call it, “fun time”).
This includes variables and functions in closures. I know, right?
To more fully appreciate that little tidbit, let’s consider the following, slightly more complex, example:
function outer() { var n = 10 function inner() { alert(n) } function alter() { n += 5 } return { inner : inner, alter : alter } } var container = outer() container.inner() // alerts 10 container.alter() container.inner() // alerts 15
In this case, calling outer
returns an object literal with 2 members: the functions inner
and alter
. Calling container.inner
the first time alerts 10, like in our original example. But, when we call container.alter
, we add 5 to the value of n
so the next time we call container.inner
, we alert the value 15 (10 + 5).
The change to n
actually happens in alter
’s containing scope, which also happens to be inner
’s containing scope. That’s why inner
has access to the changed value. Boo. Yah.
You now have no excuse for fumbling the question “What are closures?” in your next job interview. ;-)