DEV Community

Cover image for The Wacky World of Closures & What Makes Them Useful
aruna-x
aruna-x

Posted on • Edited on

The Wacky World of Closures & What Makes Them Useful

Can you guess what this prints out?

for (var i=0; i<3; i++) {
  setTimeout(() => console.log(i), 2000)
}
Enter fullscreen mode Exit fullscreen mode

... Are you sure? There's a gotcha here, and if you're not aware of it already, I'd wager this blog post is worth your time. (Hint: 0, 1, 2 is incorrect.)

Getting Closure With Javascript

To understand what's happening in the above code snippet, we have to understand closures. If you're looking for practical applications of closures, you can jump ahead.

A closure has a surprisingly simple definition: a function with access to information outside of itself, otherwise known as its "lexical environment". function addTwo() is a closure:

let x = 5;
function addTwo() {
    return x+2;
}
Enter fullscreen mode Exit fullscreen mode

And let x = 5 is in its lexical environment.

All functions in Javascript can be closures, because they automatically gain access to outer scope.

By contrast, pure functions are not closures:

function addNums(a,b) {
    return a+b;
}
Enter fullscreen mode Exit fullscreen mode

addNums does not reference any data outside of its own scope. Its data is kept in your computer's short term memory. It gets pushed onto the "call stack", executed, and then popped off the stack again. Clean, simple, easy.

On the other hand, when a function references information outside of its own scope (as with a closure), its data becomes packaged (or "enclosed") with references to all of its lexical info, and the entire package gets placed in longer term memory, called the heap. We can thank a memory management process called garbage collection for keeping the heap clear of information we no longer need in long term memory.

Despite closures needing more memory and computational power, there are some great reasons to use them (which I'll cover in a moment below).

Not All Closures Are Made The Same

Closures are particularly easy in Javascript.

You can use let over lambda to create a closure in Lisp (the second oldest higher-level programming language).

The nonlocal keyword is helpful to gain access to variables normally outside of scope in python closures.

In C# however, closures must explicitly be enclosed with its lexical environment, through "binding" variables.

You get the idea. For now, we'll continue to use Javascript.

What Makes Closures Uniquely Useful?

There is surprisingly sparse info online about uses for closures. It's odd! While I'm sure there are many more uses, there seem to be at least two compelling ones I'd like to discuss:

  • Function factories
  • Namespacing private functions

Function Factories

Function factories are functions that return other functions based on various conditions. I'd like to share how I used a function factory in a recent project. But first, let's look at a simple example.

function factory(num) {
    switch(num){
      case 3:
        return (b) => num - b
      case 4:
        return (b) => num % b
      case 5:
        return (b) => num + b
      default:
        break;
    }
}
Enter fullscreen mode Exit fullscreen mode

If we call factory(5), it returns (b) => 5 % b.
If we call factory(4) it returns (b) => 4 + b.
And if we call factory(4)(2) we can see that:

factory(4) = (b) => 4 + b

So factory(4)(2) becomes ((b) => 4 + b)(2)

Resulting in (2) => 4 + 2. Which returns 6.

The important note here is that function factories return functions that can accept even more info.

A Closure Function Factory In Use

I recently built a notes app with a react front end using semantic-ui-react. The new note form included a dropdown menu. (Bear with me here.)

semantic-ui-react's dropdown menu requires an array of options. Once I fetched data from my database and generated the options array, it looked something like this:

let options = [
    {value: 1, key: 1, text: option1}
    {value: 2, key: 2, text: option2}
    ...
]
Enter fullscreen mode Exit fullscreen mode

You can feed this array to the dropdown like so:

<Dropdown
    name="dropdown"
    multiple
    search
    selection
    options={options}
/>
Enter fullscreen mode Exit fullscreen mode

(I've simplified all of these snippets of code for readability.)

This dropdown will allow you to make multiple selections. It turns out the value attribute of semanitic-ui-react's dropdown menu is an array of values from the objects in options. I wanted to store whole objects from options in state instead.

I wanted just one change handler function for all form elements. Closure to the rescue.

Every form element executes the same function on change, like this:

onChange={(e) => handleMaker("name")(e)}
Enter fullscreen mode Exit fullscreen mode

"name" matches the name attribute of the form element it's associated with for style reasons.

handleMaker is a function factory that returns a different function based on which form element name is passed in. The function returned from handleMaker accepts the onChange event as an argument.

Here is a simplified version of the function factory I use in the app:

function handleMaker(name){
  switch (name) {
    case "note":
      return (e, {value}) => setFormData({...formData, [name]: value});
    case "collections":
      return (e, {value}) => {
        setFormData({...formData, [name]: value.split(",").map(w=>w.trim())});
      }
    case "dropdown":
      return (e, {value}) => {
        setFormData({...formData, [name]: options.filter(o => {
          for (v in value) {
            return (v === o.id) ? true : false
          }
        })})
      };
    default:
      console.error("Oops, something went wrong!");
      break;
  }
}
Enter fullscreen mode Exit fullscreen mode

There are other cases here, showing how a function factory can help handle all sorts of special cases.

Namespacing private functions

Private functions make apps more secure, disallowing ill-intentioned users from calling functions or methods that can mutate the app's state unhelpfully (or, in some cases, even inject code).

Ruby has a private keyword to make methods private. Javascript didn't until recently. But that applies to classes. When we're not inside classes (or running on IE, lol), we can still namespace private javascript functions with closures:

const namespacer = (function() {
    let num = 100;
    function changer(amt) {
      return num += amt;
    }

    return {
      public1: function() {
        return changer(100);
      },
      public2: function() {
        return changer(-100);
      },
      public3: function() {
        return num;
      }
    };
})()
Enter fullscreen mode Exit fullscreen mode

Here, we can see that namespacer is actually an object with closures as keys, since the anonymous function on line 1 is immediately invoked on the last line.

We can call the public functions like this:

namespacer.public1(); // 200
namespacer.public2(); // 100
namespacer.public3(); // 100
Enter fullscreen mode Exit fullscreen mode

But we would be unable to call changer directly:

namespacer.changer(); // TypeError: undefined is not a function
Enter fullscreen mode Exit fullscreen mode

Or access num:

namespacer.num; // undefined
Enter fullscreen mode Exit fullscreen mode

Presto! Private functions.

Closures In Interviews

If you are new to web dev and preparing for interviews, it may interest you to know that there is a common interview question involving closures:

for (var i=0; i<3; i++) {
  setTimeout(() => console.log(i), 2000)
}
Enter fullscreen mode Exit fullscreen mode

Can you guess what console.logs here?

If you guessed

3
3
3
Enter fullscreen mode Exit fullscreen mode

... you'd be right! We might expect 0, 1, 2 but that won't happen here. Each time we go through the loop, setTimeout waits a whole 2 seconds before running. The i inside of the setTimeout callback function refers to the i from the loop. Instantiating it with var gives us access to that variable even after it's done running. In 2 seconds, the loop will have run 4 times. Once i is assigned 3, it fails the condition and exits the for loop, leaving i at 3 when all three setTimeouts eventually run.

There are a number of ways we can fix this. One way is to wrap the callback function inside of setTimeout in an immediately invoked function that accepts i as its argument:

for (var i=0; i<3; i++) {
  setTimeout(((i) => (() => console.log(i)))(i), 2000)
}
Enter fullscreen mode Exit fullscreen mode

What does this accomplish? Wrapping the callback function in an immediately invoked function ensures that the current value of i is passed in and kept in the state of the setTimeout function. It is stored there for later use.

Another way we can do this involves wrapping the entirety of the setTimeout in the same immediately invoked function:

for (var i=0; i<3; i++) {
  ((i) => setTimeout(() => console.log(i), 2000))(i)
}
Enter fullscreen mode Exit fullscreen mode

This accomplishes the same result.

A Final Musing:

I'm curious to know whether there is a language in which creating a closure is impossible. So far my Googling efforts haven't gotten me far. I'd be grateful for your thoughts on the topic.

Top comments (17)

Collapse
 
ynfle profile image
ynfle

As far as I can tell (tested in node, chrome and safari), your first example prints 3 three times, not 2. This is because the variable is set to three, checked that it doesn't fulfill i < 3 and then exits the for loop and retains the value 3

Collapse
 
aruna profile image
aruna-x

Gosh, of course. Thanks for the note. Fixed!

Collapse
 
ynfle profile image
ynfle

Great post, by the way

Thread Thread
 
aruna profile image
aruna-x

Thank you so much!

Collapse
 
msk61 profile image
Mohammed El-Afifi

Nice article, especially for the pointer to how closures are used and handled in other languages. For me it was the first time to hear about nonlocal in python although I claim I've known python for quite a long time.

Collapse
 
aruna profile image
aruna-x

Thanks Mohammed :) I appreciate you telling me what was most helpful. Iโ€™ve heard this from a couple other folks who know python too.

Collapse
 
amodeusr profile image
Ricardo Magalhรฃes • Edited

Or you could simplify everything using modern javascript and not using old and deprecated var but instead let...?

It seems like the simplest solution, I didn't quite understand why you didn't talk about it.

Collapse
 
aruna profile image
aruna-x

Hi Ricardo! Itโ€™s simple really. What I was interested in was creating a toy problem that would serve for learning purposes, that would help clarify how closures (and scope) work. If you think of a different example of closures that would similarly clarify the points above, Iโ€™m happy to hear them.

Collapse
 
torkage profile image
Torkage

You should test your example :
for (let i=0; i<3; i++) {
setTimeout(() => console.log(i), 2000)
}
It prints out 0,1,2 as expected. Because you used let to declare i. Can you guess why ?

Collapse
 
aruna profile image
aruna-x

I also put the ++ in the wrong spot according to my notes. Fixed that as well!

Collapse
 
torkage profile image
Torkage

Please also check your factory example, factory(4)(2) returns an error. Your namespacer function also returns undefined for namespacer.public1(); and
namespacer.public2();

Collapse
 
aruna profile image
aruna-x

Fixed! Thanks again :D

Collapse
 
aruna profile image
aruna-x

Lol, my bad. I have var written in my notes, but typed let here when I wrote the blog post! Thanks for pointing this out!

Collapse
 
parenttobias profile image
Toby Parent

This is brillliant! Very well written, I may have to refer back to this. Often. Thanks for it!

Collapse
 
aruna profile image
aruna-x

Thank you Toby. That makes me happy. Iโ€™m glad itโ€™s useful!

Collapse
 
lowlifearcade profile image
Sonny Brown

This does seem like a good reference to have on hand. Thank you. The article is well written.

Collapse
 
aruna profile image
aruna-x

Thanks Sonny. Happy to hear itโ€™s useful.