JavaScript closures, don’t be blinded by what you see

David Moreno
Frontend Weekly
Published in
3 min readApr 1, 2023

Programmers will have a differentiator element in their interviews If they demonstrate that they master its core concepts. Here I want to share my knowledge explaining one of them: Closures. I hope it helps you to not be blinded by what you usually see in this JavaScript function’s feature.

As a frontend developer I firmly believe that you must master Javascript (commonly known as Vanilla Javascript) before start to learn any of its frameworks like Vue, Angular or React. If you dominate Javascript, it will be easier to learn any framework for sure.

If you can explain the core concepts of the language in your own words instead of simply repeating its raw definitions by heart, you will be a different and most likely one of the main candidates in your next job interview.

During my career I’ve been on both sides of a job vacancy: as the interviewer and as the interviewee. One of the classic questions I have heard in this process is:

Do you know what a closure is?

And the classic answer is:

Yes!, it is a function inside another function that remembers things.

Some others add “…things in its lexical scope”. The following is the typical example of a closure:

function createCounter (initialCounter) {
// variable in increaseCounter's lexical scope
let accumulator = initialCounter;

// function inside another function (counter)
function increaseCounter () {
// accumulator's value "is remembered" by increaseCounter
console.log(accumulator);
accumulator = accumulator + 1;
}

// counter returns the increaseCounter function reference
return increaseCounter;
};

// Here is where you think the closure is created...
const increase = counter(1);
// and then it works:
increase(); // prints 1, then accumulator => 2;
increase(); // prints 2, then accumulator => 3;

A simpler and modern version of this code would be:

function createCounter(initialCounter) {
let counter = initialCounter;
return () => ++counter;
};

const incrementCounter = createCounter(2);
console.log(incrementCounter()); // 3
console.log(incrementCounter()); // 4

And well, this is ok, this is true. But you’re blinded by what you see. It’s maybe the main use case for closures, but it’s not the main reason why we have closures.

I would say that a closure is in fact a normal function, or at least one of the main features that a function always has, since it is declared.

The closure is not a superpower that functions acquire only when they’re declared inside another function and then they’re executed outside of it.

The function borns with that ability. You have closures even with functions that are declared in the global scope.

See the following example:

// variable "a" declared in global scope
let a = 1;

// function in global scope, it remembers value of "a" in its lexical scope
function closure1 () {
console.log(a);
// "a" gets value of 2 only when closure1 is executed
a = 2;
}

// "a" changes in global scope (before closure1 and closure2 are executed)
a = 3;

// Another function declared in global scope
function closure2 () {
// closure2 remembers the last change that "a" had.
console.log(a);
}

// Each function remembers the last value that "a" got before its execution
closure1(); // Prints 3, then changes "a" to 2
closure2(); // Prints 2

We can now understand that the lexical scope of a function is simply the parent scope of that function, no matter whether it is another function or the parent of all scopes (the global scope).

Variables that are declared and/or modified in that scope are remembered by functions declared in the same scope when they’re executed.

However, to modify global variables is not a good practice and that’s why we usually see the use of closures like the first example: a function inside another function.

There are many ways to use closures: we can have nested closures, closures inside loops, even with asynchronous stuff, and also some advanced techniques, patterns and good programming practices that are based on this ability, like memoization to improve performance or module patterns to emulate private methods.

And there we have it. I hope you have found this useful. Thank you for reading.

--

--

David Moreno
Frontend Weekly

Frontend Developer from LATAM, passionate with best practices for web applications. https://twitter.com/damorenon