Why are closures so confusing?

I've seen a lot of people asking about closures on the internet.

If you ask around on forums, or reddit, you have no trouble getting an answer – The problem is the answer you get looks something like this:

Imagine you have a backpack. Inside that backpack there is a folder, a lunchbox, and some mail. Inside the lunchbox, there is also an apple. Suddenly, all of these items come to life and try to take stuff from each other, breaking this analogy. Luckily, because of closures, some of them can't.
The end.

Knowing the language is important for any developer, so learning about closures is something that every Javascript developer should do if they want to get better.

So what exactly are Closures? Why do they exist? How we use them? and what does it all means for you?

What is a closure anyway?

To put a long story short: closures are functions.

That's it. Honestly. Obviously, they are a little bit more complex than that, otherwise we wouldn't have bothered to give it a special name.

The concept of closures exist because of one rule we have in Javascript: Inner scopes are able to access everything in parent scopes. Since functions create a new scope, this can become: "Every function has access to variables and parameters from its parent functions."

The detailed explanation is that closures are an implementation detail of Javascript – In order to ensure variables from parent scopes remain in scope, functions need to keep references to them. A closure is the combination of a function and the scope the function has stored.

What closures means for a developer is that any function I create will only reference the scope where it was defined, no matter where the function is actually called.

How are we meant to use closures?

The truth of closures is that you very rarely need to "use" them explicitly. They form a core part of how functions handle scope, so you tend to use them without ever needing to think about it.

A common example of a closure in action you might be familiar with is callbacks.

//foo.js
import {sillyConcat} from './bah.js';

const globals = {};

sillyConcat('hello', 'world' , function(result) {
    //This function creates the closure, and includes a reference to globals
    globals.hello = result;
});

//bah.js
function sillyConcat(s1, s2, callback) {
	//This is where the closure is called - there is no direct access to
	//variables from foo.js, but the function runs fine anyway
	callback(s1 + s2);
}

export {
	sillyConcat: sillyConcat
};

Why do we need to know about closures?

For the most part, you don't. Except when you do. It can be important to know how functions store references to variables in parent scopes to avoid bugs and some tricky gotchas.

This is a common gotcha that involves closures.

function delayedPrint() {
	let total = 0;
	for (let i = 0; i < 4; i++) {
        total += i;
		setTimeout(function closure() {
			console.log(total);
    	}, 200);
	}
}

delayedPrint(); //expected: 0, 1, 3, 6 actual: 6, 6, 6, 6

This happens because each of our setTimeout functions takes a reference to the total variable, but doesn't check its value. By the time the function is called, the loop has finished running, and total equals 6 – so each function prints 6.

To get around this, we need to copy the value of total to a new variable that isn't stored in the parent scope. We can do this by passing it as a parameter to the function.  

function delayedPrint() {
	let total = 0;
	for (let i = 0; i < 4; i++) {
        total += i;
		setTimeout(function closure(total) {
			console.log(total);
    	}, 200, total);
	}
}

delayedPrint(); //expected: 0, 1, 3, 6 actual: 0, 1, 3, 6

We could also achieve this by creating another function and calling it immediately (an IIFE).

function delayedPrint() {
	let total = 0;
	for (let i = 0; i < 4; i++) {
        total += i;
        (function(total) {
        	setTimeout(function closure() {
				console.log(total);
    		}, 200);
        })(total);
	}
}

delayedPrint(); //expected: 0, 1, 3, 6 actual: 0, 1, 3, 6

If you're interested in learning more about closures, MDN has a great article.

Also be sure to check out my guide on keeping your Javascript clean: Introduction to Readable Javascript.

Show Comments

Sign up for the Newsletter!

Rather than checking back, drop your name and email below and get more helpful articles like this sent straight to your inbox.

100% Spam free - Guaranteed. Unsubscribe at any time.