Home > Javascript > Closures for Dummies (or why IIFE !== closure)

Closures for Dummies (or why IIFE !== closure)

October 2nd, 2011

[Update: this article is bogus! I misunderstood the criteria that determines when a closure becomes a closure! To be clear: in JavaScript, every function effectively creates a closure. Nuf said. Go ahead and read the rest if you’re bored.

There are lots of blog posts and online articles about javascript closures. Many of them are well written. Closures have also been discussed at length with Brendan Eich. I really shouldn’t have to write another blog post. Yet, here I am.

Why? Because I still see many people misusing the term, “closure”. Specifically, I see people confusing IIFEs with closures.

I was going to see if I could craft a twitter-friendly 140-char explanation of the differences between IIFEs and closures. A noble cause, for sure, especially since I suspect that the reason that there’s so much confusion is that many of the explanations are either tl;dr or too technical.

140 characters may have been too ambitious. Let’s see if I can do it in 140 words or less. Starting… now. Well, not including code snippets, mmkay? OK. Starting… now.

[update 2011-10-04] Turns out I am wrong! Who would have guessed that??!?! I’ll post an update shortly to explain that my definition of closures is slightly off. However, the message I want to convey is still the same: closures don’t matter except under specific circumstances.

Definitions

A closure is what happens when you’ve made a function’s private variables (and/or embedded functions) available to outside code. That’s it. There’s no specific pattern you need to use, it just happens. That said, there are lots of patterns that take advantage of closures.

IIFEs are simply a way to create a private set of variables and functions. You create an IIFE by constructing a function expression that contains some private variables and/or embedded functions — and then executing it. Immediately. Most meaningful IIFEs create a closure, but they don’t have to.

Stop. Not bad. Only four tweets-worth.

Examples

Let’s take a look at a few examples.

// safeSnapshot returns a function that filters out 
// embarrassing tweets. tweets are only filtered
// when needed, potentially saving cpu time if they 
// are never needed.
function safeSnapshot (tweets) {
    var snapshot = tweets.slice(0); // shallow copy
    return function () {
        var safelist = [];
        for (var i = 0, len = snapshot.length; i < len; i++) {
            if (snapshot[i].text.indexOf('#poopin') < 0) {
                safelist.push(snapshot[i]);
            }
        }
        return safelist;
    };
}
 
// ... later in the code, we give an object (tweetConsumer) 
// a handle to a function that it may use to get tweets 
// (via its tweetFetcher property).
// It may or may not ever call this function, but it has it in
// advance.  (Use your imagination to dream up a reason
// this could be useful.  There are many.)
// allTheTweets is just an array of tweets we got from 
// somewhere else. (Again, use your imagination. I know
// you have one.)
tweetConsumer.tweetFetcher = safeSnapshot(allTheTweets);

When safeSnaphot is invoked, it returns a function. The returned function creates a closure around safeSnapshot‘s local variables because the returned function uses the variables internally (actually it just uses snapshot). The values (or references) held by these variables will stay in memory as long as something is holding a reference to the returned function.

At the end of that example, we see that we’ve handed a reference to the returned function to tweetConsumer as its tweetFetcher property. As long as tweetConsumer exists and nothing has reassigned its tweetFetcher property, the snapshot array will continue to exist in memory, waiting forever, if necessary, or until Chrome crashes. (omg shaddap! IE-bashing is so 2010 ya know)

Code snippet #2:

var squashed = (function (doc) {
    var nodes, howMany = 0;
    // find all links that open another friggin tab
    nodes = doc.getElementsByTagName('a');
    for (var i = 0, len = nodes.length; i < len; i++) {
        if (nodes[i].target != '') {
            // heh. this'll fix em. oh yeah! dom0 is so hawt.
            nodes[i].onclick = function () { return false; };
            howMany++;
        }
    }
    return howMany;
}(document));

Here’s a useful IIFE. It’s definitely a function expression since we’re using it inside an expression. It’s also being invoked immediately (see last line).

This is not a closure though. Why? Simple: there’s nothing capturing local variables or embedded functions. The local variables, nodes and howMany, are only used internally and there are no functions being returned to outside code.

What about the variable, howMany? It’s being returned!

No. I’ts not. It’s value is. howMany‘s value is being returned and placed in the [outer] variable, squashed.

What about the onclick function?

The onclick function would create a closure if it referenced variables in its scope, but it doesn’t.

What’s it all mean?

IIFEs are a useful construct. They’re a great way to isolate variables (and can also be used to capture copies of transient variables, but that’s too tl;dr for this discussion). IIFEs can also create closures. In fact, any function can create a closure if it returns a function — directly or indirectly — and uses local variables.

Closures are also very useful, but you don’t create closures via an explicit pattern. Closures just happen under the right circumstances and are used in many different patterns.

Note to javascript zealots and trolls

Yes, I know I skirted around a ton of details. And, yes, the terminology I used does not accurately reflect the state of the machinery under the hood. I did it to try to simplify the concepts. I think I portrayed closures accurately despite the simpler terms.

I also skipped dozens and dozens of great use cases. If people want to see more use cases, they can google it.

That said, if you’ve got a useful resource (link) or some additional info you think could be useful to devs who are learning about closures, please leave a comment.

— John


[update 2011-10-03]

I guess I inadvertently hijacked somebody else’s blog title. Although, this person is misusing the term “closure”, too.

I also got some twitter-lashing from some very smart people in response to this blog post. Furthermore, it seems just about every article on the web claims that simply placing a function inside of another function creates a closure. However, they are all wrong. Read on:

Read my lips!

The closure doesn’t exist until the local scope is externalized.

Read that again. This is the essence of the problem. A closure happens at run time under certain circumstances. It’s not something you construct at design time.

Here’s yet another example to show what this means:

(function () {
    var practicePhrase, practiceCount;
    practicePhrase = "Park the car in Harvard yard.";
    practiceCount = 10;
 
    function repeat () {
        alert(practicePhrase);
        if (--practiceCount >= 0) setTimeout(repeat, 1000);
    }
 
    // this is the important part. pay attention. :)
    if (new Date().getDay() != 2) {
        setTimeout(repeat, 1000);
    }
 
}());

In this example, we’re using setTimeout to “externalize” the local variables, practicePhrase and practiceCount. setTimeout doesn’t externalize in the usual way. We’re not returning a function to other javascript somewhere. Instead, we’re returning a function to the browser’s event queue mechanism. It works the same way, though.

Well, if you fully grokked this example, you’ll notice that the setTimeout sequence (which executes 10 times) is only initiated if this isn’t Tuesday. This means that the example code does not create a closure on Tuesdays.

Why? Because on Tuesdays, the repeat function — the one that would create the closure — is never externalized.

Simply placing a function inside of another function does not create a closure. It’s just an instance of function scope, which just means that functions can access variables in enclosing functions.

Some tl;dr articles that got it right:

MDN – Closures (a bit of confusion by the author, but read carefully)
Closure (wikipedia)

And now, I fear, this article is tl;dr, too.

[updated again 2011-10-02] I added some clarification about the function referenced by the onclick handler in the second example. (thx @brianarn!)

Javascript

  1. October 4th, 2011 at 02:58 | #1

    I wasn’t going to respond up until the point where you felt you needed to clarify in high detail…

    A closure is an inner scope that has a references to its outer scope.

    It happens any time a function is created (technically, even global). That actually even goes for “IIF”, since the function is created first (has references to its outer scope) and THEN is called (two separate “events” in js). The call is not part of the function decl/expr and therefore comes next. Between the two there’s a gap and that’s where you’d say there’s a closure. After that call the reference to the function ceases to exist and the garbage collection tears down the function and with that the closure too.

    In current js, any function is a closure because they all at least reference the global scope. And regardless of how you construct a function (expr/decl), the function will “exist” for at least some time, during which the closure is in effect (that is, during which the garbage collection cannot remove any resource referenced from within that function). Only when the reference of the function is removed by the GC, so are the resources referenced by that function and thus the closure effectively ceases to exist.

    An inner scope with a reference to its outer scope for as long as the inner scope exists. That is all. Could fit 1.5x in a tweet ;)

  2. troll
    October 5th, 2011 at 20:42 | #2

    So are you going to update this post? I saw that you were corrected on Twitter. I think the ECMA Script spec even says that a closure is created every time ANY function is created, although in some Lisps there are compiler optimizations that can be done to avoid creating excessive closures which lead to GC, I believe that this statement is completely wrong: “The closure doesn’t exist until the local scope is externalized.”

  3. troll
    October 5th, 2011 at 20:43 | #3

    oh nvm you did, silly troll.

  1. October 10th, 2011 at 02:09 | #1
Comments are closed.