A Graphical Explanation Of Javascript Closures In A jQuery Context
Posted February 4, 2009 at 10:10 AM by Ben Nadel
Over the weekend, I was working on my in-depth jQuery presentation for the New York ColdFusion User Group. As part of the presentation, I wanted to discuss the beauty of Javascript "closures" and how jQuery makes tremendous use of them. Javascript closures can be a very hard thing to wrap your head around, especially when you are faced with vague definitions like:
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).
I mean, come on, what the heck does that mean? I know a bit about closures and that definition doesn't make sense to me. Like I said, closures can be a hard thing to really understand. As such, I wanted to try and explain them [closures] with the help of some graphics because, as they say, a picture is worth a thousand words.
First, let's look a simple jQuery demo:
| | | | ||
| | ![]() | | ||
| | | |
In this code, we are adding click event handlers to all of the links in the document. These click event handlers, when triggered, will alert the index of the given link in the context of the entire set of document links. Of course, the intent of the code is secondary to the demonstration. What's of primary importance here is structure of the code. Take a look at the functions that are being defined:
| | | | ||
| | ![]() | | ||
| | | |
Notice that we are defining three anonymous methods in this demo. Each of these methods is defined within a parent context. The outer-most method is defined in the context of the window (theoretically based on the visible code); the middle method is defined in the context of the outer-most method; and the inner-most method is defined in the context of the middle method.
In Javascript, a given context always has access to its parent context. To be honest, the mechanisms behind this visibility are a bit beyond my purview, but I know it has something to do with scope chains (or is it prototype chains - definitely some sort of chain). As such, each method in our demonstration has access not only to its locally defined variables, but also the variables available in its parent context:
| | | | ||
| | ![]() | | ||
| | | |
Notice that in the inner-most method, it has to move up one scope in the parent chain to find "intLinkIndex," but it has to move up two scopes in the chain to find the "jLinks" variable. While it might not be obvious, the two-parent-context-jump is following the same exact rules - first, the inner-most method looks for jLinks in its own context. Since it's not a local variable, it can't find it. Then, it asks its parent context (the middle method) for the variable. The middle method checks its local scope and can't find it, so it passes this request up to its parent context, the outer-most method. And, because each context has access to its parent context, this variable request is easily passed up the context chain.
Ok, so far so good; now, let's really get into closures. Keeping this whole parent context concept in mind, take a look at where these defined methods are ending up:
| | | | ||
| | ![]() | | ||
| | | |
As we are defining these anonymous methods, we are using the each() method and the click() method to pass these functions away from their parent context. Where are they going? We don't know - they're being passing into another context altogether. Sure, from the demo code, it looks like they are getting passed to jLinks and jThis respectively; but, the fact is, once these method references are passed into those foreign contexts, we can't say with certainty how they are being used. Are they transient? Are they being cached somehow in the window object for the entire life of the page?
These are questions that we can't answer; but, more importantly, these are also questions that the browser's memory management system can't answer (speaking with personification). And, because it doesn't know where these methods are ending up, it can't garbage collect (destroy) the original parent context of the method definitions. That's the power and beauty of the Javascript closure right there - see, even though the methods are being passed away from their original context, due to the scope chains in place, they still have access to their parent context and to all the variables available in their parent context.
So, the real message to take away here is that when you pass a method reference out of the context in which it was defined, the method still has access to its parent context. And, once you understand and embrace this, it can be leveraged in some really cool ways (as seen in this jQuery example). This is not the most in-depth explanation of closures, but hopefully these graphics have helped you to better understand the closure mechanism.
Reader Comments
Ben: Good explanation -- one of the best explanations I've seen.
There is also a great video on Advaned JS by Douglas Crockford that goes into details on how this works behind the scenes. Check it out.. its available in the YUI theater: http://video.yahoo.com/watch/111585/1027823
I second Brian's comment. As I was reading through the post I was thinking I'd have to find the same video.
Pretty much all of the Crockford videos on Yahoo! are worth your time. Some of them get mind-bendingly complex, but they'll really open your eyes.
Having said all of that, I'll also say that your post is an excellent explanation of a very confusing concept. Very nicely done.
Thanks guys. I'll have to check out the YUI videos. I think I went there once and was just overwhelmed with the amount of content :) Time to get over that fear!
Looking forward to the presentation next week!
@Aaron,
Oh heck yeah!
You're a star!!! That's a really good explanation!!!!!!!! Thanks for that Ben
What makes most of this possible is that JavaScript is "lexically scoped." This means that something is in the scope in which it was defined--not where it ends up. Wherever I wrote the function, it is in (and stays in) that scope.
Lexical scoping and lambda (anonymous) functions combined with JavaScript's awesome chaining capabilities also make for some brilliant ways to namespace your projects and emulate class-like method/property access-control (i.e. public and private). It is what makes jQuery possible!
Once you wrap your head around these concepts, JavaScript becomes a sexy and extremely flexible and powerful language. Yes, I called a programming language sexy!
...continued from my previous comment...
I forgot to add that the chaining capabilities allow for "self executing" lambdas which is one of the coolest namespacing tools available.
Great explanation, love the images. Nice to see such a pragmatic explanation, without to much detail to confuse the topic.
@Jim,
I have seen one or two examples of public/private emulation with Javascript. At the time, I didn't understand enough Javascript to see how that was working. Maybe it would be different now.
Yeah, it is sexy stuff!
A very thorough explanation of Javascript closures can be found here (take a deep breath) :
http://www.jibbering.com/faq/faq_notes/closures.html
> A "closure" is an expression (typically a function) that can have free
> variables together with an environment that binds those variables (that
> "closes" the expression).
> I mean, come on, what the heck does that mean? I know a bit about
> closures and that definition doesn't make sense to me.
too bad, because it's exactly what a closure is !
Free variables are identifiers in the function body that are not parameters.
An environment is a list of names and their associated values, used to evaluate expressions.
So there you have it : a closure is an expression that can look up an environment for the values of its free variables or , to use your own terms, an expression that has "access to their parent context"
> As we are defining these anonymous methods, we are using the each()
> method and the click() method to pass these functions away from their
> parent context. Where are they going? We don't know - they're being
> passing into another context altogether. Sure, from the demo code, it
> looks like they are getting passed to jLinks and jThis respectively; but,
> the fact is, once these method references are passed into those foreign
> contexts, we can't say with certainty how they are being used. Are
> they transient? Are they being cached somehow in the window object
> for the entire life of the page? These are questions that we can't answer...
they can be answered though :
in the case of jThis, the anonymous function is registered as a handler for the onclick event (think about it as being referenced by the <a> element itself)
in the case of jLinks, the anonymous function is executed for each (index, obj) in jLinks, ... maybe that's what you call transient ? In any case once that block has executed, that function is gone...
@Zorg,
That is a great link and is, in fact, where I got the original definition for closures.
As far as where the functions go, I am sorry if I did not communicate well; I understand that yes, we can look into the Click event wiring and actually see where it goes; what I mean to get across in a more abstract way is that when a method is passed out of its parent context, we should not assume anything about where it goes or how long it lives. And, in doing so, we can start to become comfortable with the idea that the variables in its parent scope must be kept around in case the method reference is ever executed.
I wanted to get people comfortable with the idea of closures at a high level, not so much with the specifics of the behind the scenes.
@Ben
> I wanted to get people comfortable with the idea of closures at a high
> level, not so much with the specifics of the behind the scenes.
Sorry, I missed that :)
@Zorg,
Nothing to be sorry about at all my friend. Closures are a really complex concept for people to get. The more insight that people such as yourself can offer, the better off we all are going to be.
Too complicated.
Where did you learn this? Do you know of any good resource or book for learning functional JavaScript from ground-up?
@dl
JavaScript: The Good Parts
Unearthing the Excellence in JavaScript
By Douglas Crockford
This is good to learn and many thanks to Douglas.
@FM,
I've heard that that is a very good book.
I've translated your article into Russian. Thought you don't mind.
All the backlinks are proveded. Here is the full version: http://interpretor.ru/js_closures/
Thank you for the great article!
@Andrew,
That's awesome!! Glad you felt it was worthy :)
+1 Douglas Crockford : this is the best book about the functional core of JS. (don't miss his videos 7 on Javascript + 3 on DOM).
for the OO model of Javascript see Liberman paper on prototype-based inheritance and see the papers on Self by Ungar.
all this is freely available online
@Zorg,
Do you have a link to the Ungar paper?
http://selflanguage.org/documentation/published/index.html
Self the power of simplicity, is a good place to start
http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.html
for the Lieberman paper
This was great to understand. Thank you
I think jQuery convert all the $("a") into objects.
and then jLinks.each assign jThis.click method to each of the "a" objects.
Thank you, that's the best explanation I have seen.
Thanks for this, nice bit of graphical explanation but now I need to know more... :)
@Joel, @Julian,
Glad you guys are liking it.
Images aren't appearing!
@Richard,
My server has been having some issues lately. Try hitting this blog post again and it should work. Sorry about the dips in performance.
I especially liked your site posts, and so on through all of them beautiful
I loved this explanation of closure with scope-chaining. Clearest ever article on closure!!!
Thanks Ben
vimal
Germany
man, this was clear. As I m getting more into JavaScript seriously, I realize that I can see the big picture more easily and It makes me really damn happy.I thought that closures were more complicated than that.Man, javascript is awesome. It really is the punk rock language!
Ben,
Loved this.... I recommended it to several folks who (like me) tend to see closures as just out of reach mentally :)
-Mark







