Closures in JavaScript in simple terms (and real life examples)

Igor Łuczko
6 min readAug 1, 2020

Closures are special thing in the JS world. It’s time to finally understand them.

Many agree that closures are confusing at the beginning, but once you put some time into understanding them you will find them perhaps even intuitive.

There are lot’s of benefits coming from understanding them i.e.:

  • In opinion of many software leads, understanding closures is one of these things that separate the junior from the rest.
  • There are patterns coming from functional programming i.e. partial application that make use of closures.
  • if you are a React girl/guy, the React Hooks rely on closures
  • It’s almost a certain question to be asked on JavaScript job interview.
  • many other benefits coming from understanding closures

Yet I still see developers working for 5 and more years and ignoring the concept of closures — and they only refresh the knowledge or memorize the answers before a job interview.

Introduction

Developers often learn best by tracking down the examples.

In this article we will:

  • cover a bit of the theory regarding JavaScript closures (just enough to be productive and understand what are we writing)
  • look into examples in more detail

By the end of the article we will be able to understand what is a closure and how we can benefit from it in the JavaScript world.

What’s a closure in JavaScript

In simple terms, closure gives us access to an outer function scope from an inner function. The closure in JavaScript has three scope chains:

  • it has access to its own scope (between it’s own curly brackets)
  • it has access to the outer function scope
  • it has access to the global scope

In JavaScript, closures are created every time a function is created, at function creation time.

A simple example to begin with

function outer() {

const b = 50;

function inner() {
const a = 100;
console.log(`a is ${a} and b is ${b}, the sum is ${a+b}`);
}
return inner;
}
// first invocation of outer
const fnFirst = outer();
// second invocation of outer
const fnSecond = outer();
// what's inside?
// note console.dir() and not console.log() command
console.dir(fnFirst);
console.dir(fnSecond);

To understand what is closure let’s walk through this example.

Function outer() invoked for the first time

  1. We create variable b. The scope of variable b is limited to the outer function. The value is set to 100. To keep things more readable, I’ve initialised it with const. This means, that the value cannot be reassigned.
  2. Then we declare function inner — nothing is executed.
  3. Next, we find return inner. It turns out that inner is a function and as such we return the function body.
    Please note that the statementreturn inner does not execute function inner. Function is executed only when followed by () i.e. inner().
  4. The content returned by return inner is stored into fnFirst variable.
    In this case, it’s the body of function inner.
  5. Function outer() finishes execution.
    All variables within the scope of outer() no longer exist.
    This is very important to understand, so I will repeat that. :)

Once a function completes its execution, any variables defined inside the function scope no longer exist.

This means that our variable b declared inside outer function, exists only when outer function is being executed.

When we execute the function for the second time, the variables of the function are created again (from scratch).

Let’s track down what happens when we invoke function outer for the second time (and store it into different variable).

Function outer() invoked for the second time

  1. We create variable b. The scope of variable b is limited to the outer function. The value is set to 100.
  2. Then we declare function inner so nothing is executed.
  3. Similarly, return inner return the body of inner — remember the function is not called.
  4. The contents returned by the return statement are stored in fnSecond.
  5. Function outer() finishes execution.
    All variables within the scope of outer() no longer exist.

Here comes the important bit (once again but it’s worth repeating).

When the outer() function is invoked for the second time, the variable b is created from fresh.
The variable b cease to exist once outer finishes executing.

The part that is confusing the devs

So far it looks rather clear.

We have returned our function into two different variables fnFirst and fnSecond. These two variables are in fact functions, which we can verify with help of typeof.
The variables fnFirstand fnSecondboth have the function body of inner

console.log(fnFirst);// outputƒ inner() {
const a = 100;
console.log(`a is ${a} and b is ${b}, the sum is ${a + b}`);
}

Here comes the tricky part

The console.logwants to print variable b.

How we will be able to access the variable b which was declared in the scope of function outer ?

An example to help us:

// continuedconst fnFirst = outer();
const fnSecond = outer();
// for the first time
// output -> a is 100 and b is 50, the sum is 150
fnFirst();
// for the second time
// output -> a is 100 and b is 50, the sum is 150
fnFirst();
// for the third time
// output -> a is 100 and b is 50, the sum is 150
fnFirst();
// for the first time
// output -> a is 100 and b is 50, the sum is 150
fnSecond();

The following happens on the invocation of fnFirst().

  1. Variable a is created, it’s value is set to 100.
  2. In the second line, the function tries to add a + b.
    Variable ahas just been created, that part is clear.
    But there is no b in the function body.
    We’ve said earlier, that the b variable exists only when function outer is executed.

We still don’t know how does inner function knows about the variable b.

Closures to the rescue

If we would do console.dir(fnFirst) , we would see something similar in the console:

Closures in JavaScript — console.dir(fnFirst) output take note at Closure

We can see that there is a Closure that has variable b = 50

That’s our answer!

The inner function has preserved the value of variable b and continues to closure it.

The inner function preserves the scope chain of the enclosing function at the time the enclosing function was executed.
In simple terms, this means that inner function can access outer function variables with help from closure.

As we have said in the beginning.
The inner function has access to three scope chains:

  • the inner scope i.e. a
  • the outer scope i.e. b
  • the global scope

In JavaScript, closures are created every time a function is created, at function creation time.

More difficult example

In the job interview you will be probably be given a code snippet similar to that one:

Then the interviewer would probably ask you the following questions:

  • tell me what this snippet is? (we already know it’s closure)
  • walk me through what’s happening? (most important part)
  • what will be the output in the console?

To answer that questions, let’s walk through what happens when we invoke fnFirst for the first time (let’s remember that we are looking at inner function as this is what has been returned):

fnFirst invoked for the first time

  1. Variable c is created and initialised as 20.
  2. Message is logged into the console with a,b,c variables.
    We already know that variables a and b are available and come from the closure.
    We can think of it like a(first_time) and b(first_time).
    Hence a = 10 and b = 100.
  3. The variable b, coming from the closure and c variable coming from inner function are incremented.
  4. When the function fnFirst completes execution, the variables inside it (c ) cease to exist.
    However, the value b was preserved to the closure and as such is preserved to the next function call.

fnFirst() invoked for the second, third, and … time

It’s similar as the first invocation, the only difference is step 2 — the value of b variable is already incremented.

  1. The variable c is created and initiated as 20
  2. The variable b, coming from the closure, is incremented by 1 hence it’s value is 101. In next function call it will be 102, then 103and so on.
  3. b variable, coming from the closure and c variable coming from inner function are yet again incremented.
  4. When the function fnFirst completes execution, the variables inside it ( i.e. c ) cease to exist. However, the value b was preserved to the closure and as such is preserved to the next function call.

You can also use closures for a pattern called partial application but that is a story for another article.

Summary

Closures are initially difficult to understand especially when introduced with advanced jargon definitions.

If that’s the case, we can try to ignore the jargon initially, go through some examples, understand them, and then come back to the jargon.

With a bit of practice you will probably come to realise that closure is in fact intuitive, powerful tool for us.

PS

There is no escape from closure s— you still have to understand it on a job interview (or at an exam).

--

--

Igor Łuczko

Technical Lead. Seeking to make a change. Doing quality work that matters for people who care.