Understanding JavaScript Closure

Hello Amazon App

Learn about Closure, JavaScript execution context, the power of Closure using two practical examples.

Table of Content

How to read this article #

This article refers to and explains the code snippets and diagrams. The effective way to understand content would be to look at the related code snippets and diagrams along with reading. I would suggest to duplicate the tab and keep the related code snippet and diagrams in a new tab and refer as required.

How I learned Closure #

It all started when my former colleague and I discussed this JS concept a few years back. Besides, I came to know it is one of the popular interview questions.

I read about Closure a couple of times. Most of the time, the author explained the definition without any concrete scenario to use it.

Even in the many interviews, if you provide below definitions they will move to the next question.

Definition 1
“A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain. ­– JavaScript is Sexy

Definition 2
“An inner function which has access to outer function’s variables even after the outer function has returned”.

Let’s demystify these definitions using below code snippet.

The function generateNickNames is the outer function. Even after the return of the outer function, the inner function getNickName has access to the names identifier.

I get the example and appreciate it but what I fail to understand is where in my real codebase I should apply this concept.

function generateNickNames() {
  const names = ["Leonard", "Sheldon", "Penny", "Raj", "Bernadette"];

  function getNickName(index) {
    const name = names[index];
    const nickname = fetch('https://getnickname.com/us/name').then(data => data.name);
    return `The nickname for ${name} is ${nickname}.`;
  }

  return getNickName;
}

const bigBangCharacter = generateNickNames(); // at this point generateNickNames is returned
const randomCharacterNickName = bigBangCharacter(2);

I can understand all these definitions but when you don’t know the real-life scenarios, you can’t understand how powerful the concept can be!

I stumbled upon Will Sentance’s course on FrontendMasters. He talks about Closure and how powerful it is. He shared a few examples that provide direction to utilize Closure more and more.

I really loved the way he taught core concepts of JavaScript and I will borrow his drawing and teaching method.

Understanding JS execution context #

Let’s forget definitions and the example we read above. We will now build our understanding from a very fundamental level.

Let’s take the following code example and see how under the hood JS is executing it.

function moviesForWeekend() {
  const movies = ["Monsieur Lazhar", "The Notebook", "P.S. I Love You", "Yes Man", "Before Sunrise"];
  return movies[4];
}
const movie = moviesForWeekend();
console.log(movie);

Say the browser starts executing this code and it will go line by line as JS is single-threaded language. It will execute the code from top to bottom and will not go to the next line until it gets the result from the current executable line.

A diagram depicts execution context for moviesForWeekend function

(Click on image to zoom execution context diagram)

It will first hit function moviesForWeekend() { … } and keep it in global memory with function name moviesForWeekend as an identifier. It will then move forward as moviesForWeekend is function definition and not execution. Execution occurs when you call a function with parenthesis.

In the diagram, see the first line of Area A.

It will go to the next line where we declare a constant identifier called movie and the value of this identifier is whatever moviesForWeekend() would return.

JS will now execute the moviesForWeekend() by referring to previously-stored function in global memory. We are executing a function at this point by using parenthesis.

As mentioned earlier, it will wait till moviesForWeekend() function gets executed, return a value, and store it in the movie constant.

Now all the global execution context will halt and we will move to the local execution context to execute moviesForWeekend function.

Once the function is called, we push moviesForWeekend to our Call Stack. (Area B in the diagram).

What is the call stack?
A Call Stack is a mechanism that JavaScript uses to track what function is currently running, what’s next function to call etc.

Let’s now go inside the moviesForWeekend function and see how it works.

An identifier movies is defined and stored in local memory which has a collection of few movie names.

The next line takes the 4th index element from the movies array and returns it. So the return value of moviesForWeekend() is "Before Sunrise”.

How can you claim that the movies is stored in local memory?

I’m glad you asked. If it is in global memory then you can access movies constant outside this function but if you log it then it will be undefined.

This returned value will be stored in the movie.

The thread of execution will go to the next line and log “Before Sunrise” in the browser console.

What is Thread of execution?
JavaScript steps through the code line by line and executes them. This is known as Thread of execution. This is what makes JavaScript single-threaded.

This is how a typical JS program executes in the browser.

Now, let’s see what happens with the below example.

function moviesForWeekend() {

const movies = ["Monsieur Lazhar", "The Notebook", "P.S. I Love You", "Yes Man", "Before Sunrise"];

function getMovieWithReleasedYear(index) {
    const releaseYears = [2011, 2004, 2007, 2008, 1995];
    return {
    movieName: movies[index],
    year: releaseYears[index]
    }
}
return getMovieWithReleasedYear;
}

const movie = moviesForWeekend();
const movieMeta = movie(4)
console.log(movieMeta);

Let’s do the last exercise again and understand how this example works.

The JavaScript will hit moviesForWeekend and store the function in global memory. After that, it will hit movie constant and call moviesForWeekend function.

Now we will execute moviesForWeekend and create a local execution context for it.

A diagram depicts execution context for moviesForWeekend function as Closure function
(Click on image to zoom execution context diagram)

A constant is declared with identifier movies and has a movie list. It will go to the next line where it will store the getMovieWithReleasedYear function definition in local memory.

The moviesForWeekend will return getMovieWithReleasedYear, which will get stored in the movie identifier. After this, the local execution context will get destroyed.

The thread is in the global context again and it will go to the next line.

In the next line, we declare a constant called movieMeta and call the inner function getMovieWithReleasedYear.

What? You said the local execution is destroyed then how would getMovieWithReleasedYear will be accessed?

That’s the beauty of JavaScript. When the outer function returns something, it not only returns the inner function but also other data with it. It can be identifiers from outer function, inner function, or the local variables. All these will be stored in the Scope of returned function in the global execution context. In this case, the movies constant stored in the Scope.

Now when we call movie(4) then it will check in the global execution context, refer to the function and begin executing it.

Now we will have a local execution context for getMovieWithReleasedYear.

A diagram depicts execution context for getMovieWithReleasedYear closure function
(Click on image to zoom execution context diagram)

We will execute this function now.

We declare a constant releaseYears and store release year list corresponding to movies.

We have return keyword in the next line where it takes index value passed from the movie(4) and returns an object with movie name and corresponding year.

Now we have value in movieMeta and we hit the next line.

The next line simply refers to the movieMeta value and logs it in the browser console.

What’s the difference in this example and the previous one?

The first example was regular JS function and this one is Closure. In the Closure, we get access to outer function’s variables and arguments. These all are stored in the inner function’s Scope when the inner function is returned.

This implies that we can persist the variable changes in the global context when we use Closure. This persisting data in its Scope is what makes Closure a powerful concept.

Do you now understand the above definitions on Closure?
I bet you do or drop feedback to improve this article.

We will take a small example to understand how powerful is Closure.

How powerful is Closure? #

What makes Closure powerful? The capability of persisting data.

If you see in the last example, when the inner function returned it took all the data used by that function in its scope. What does that mean?

It means now data is persistent in the inner function’s scope (even though the outer function is returned) and we can perform operations on them. Let’s take an example.

Note: There are a few types of Scopes in JavaScript such as Global, Local, Closure, etc. We are talking about the Closure Scope in this example.

function counter() {
  let count = 0;
  function increment() {
    count++;
    return count;
  }
  return increment;
}

const count = counter();
console.log("Result:", count()); //Result: 1
console.log("Result:", count()); //Result: 2
console.log("Result:", count()); //Result: 3

See it incremented the count which was in its scope. If you put debugger then developer tools will show you Closure Scope.

A Chrome DevTools screenshot showing closure value on right side in Source tab
(Click on image to zoom developer tools screenshot)

Do you find the possible use cases that you can do when you have persistent data? How about writing a function that runs only once? How about creating a simple generator function?
Let’s write them from scratch using the concept we just learned.

I hope now it is clear how powerful the Closure concept is!

Building simple jQuery one() function #

What is one() function? A function that will call a passed function at most one time.

function one(func) {
  let count = 0;

  return function() {
    if (count > 0) {
      return `You called '${func.name}' once. Permission denied to execute. `;

    } else {
      count++;
      return func.apply(this, arguments);
    }
  }
}


function getMovieofTheWeek() {
  return 'Contagion, 2011';
}

const result = one(getMovieofTheWeek);

console.log(result()); // "Contagion, 2011"

console.log(result()); // "You called 'getMovieofTheWeek' once. Permission denied to execute."

console.log(result()); // "You called 'getMovieofTheWeek' once. Permission denied to execute."

Let’s see how the browser will read this code.

It will first encounter with one and keep it in the global scope (as this is function definition and not execution). Then the thread of execution will encounter with getMovieOfTheWeek and again it will keep this in the global scope.

It will go to the next line and store the const result in the global scope and execute one function. It will take the getMovieOfTheWeek definition from the global scope and pass it to one function.

An execution context is created by one function and now we will see what’s going on in the function.

It will store the count in the local memory and return the anonymous function. When it returns the function, it takes two things with it in this case. Parent function’s arguments and parent function’s variable count.

Both these things are stored in the scope of returned function which is the const result.

The one function is returned and the execution context is destroyed.

The thread of execution will move to the next line.

console.log(result())

This will create a new execution context for anonymous inner function. It will look up count value in local memory.

It won’t find value there then it will look up in its scope.

The scope has a function definition and variable.

It will go in if clause and compare. As the condition is false then it moves to else block and increment the counter. It will see if the counter is in local memory. It won’t find so it will again look up in scope and increment there.

It will call the provided function and return its value. The execution will get destroyed and it will log the return value of getMovieOfTheWeek function.

The thread of execution will go to the next line.

Now when 2nd time you call then it tries to find the variable in execution context but it doesn’t get it. It then moves to its scope. Now, in the scope, the variable is already incremented when it called the first time so next time it will return a warning.

Building simple generator function #

A generator function is a special type of function (represented by an asterisk next to the function name). Unlike traditional functions, it doesn’t begin execution right away, instead, you need to call next() method attached to it. The next() method returns an object with two properties –

function* getPizzaIngredients() {

  yield "Pizza Dough"

  yield "Olive oil"

  yield "Cornmeal"

  yield "Cheese"

  yield "Mushrooms"

  yield "Tomato sauce"

}

const pizza = getPizzaIngredients();

console.log("Ingredient", pizza.next()); // Ingredient {value: "Pizza Dough", done: false}

Though this information is enough to understand our example and if you want you can quickly read an article on the generator and come back here.

Notice, when you invoke generator function you get the next() method attached to it. Let’s implement it.

function getPizzaIngredients() {
  const ingredients = ["Pizza Dough", "Olive oil", "Cornmeal", "Cheese", "Mushrooms", "Tomato sauce"]

  let index = -1;
  let isDone = false;
  return {
    next: function() {
      index++;
      isDone = index > ingredients.length - 1;
      return {
        value: ingredients[index],
        done: isDone
      }
    }
  }
}

const pizza = getPizzaIngredients();

console.log("Ingredient", pizza.next()); // Ingredient {value: "Pizza Dough", done: false}

Let’s see very briefly what’s going on.

We call getPizzaIngredients() which in turns return us an object which has next() method attached to it and a field to indicate if we yielded all the result.

When we call the next() method, it takes the corresponding index and isDone variable with it and stores it in its scope. At each next() execution we increment and check for isDone flag and return them.

This is a very simple version of the generator.

That’s all from this article.

We have covered how JS executes a function, we know Call Stack, Thread of execution, Local memory, and Closure scope. We have written two simplified version of Closure functions from scratch.


Spotted a typo or technical mistake? Tweet your feedback on Twitter.
Your feedback will help me to revise this article and encourage me to learn and share more such concepts with you.

Thank you Siwalik for proofreading it.

 
15
Kudos
 
15
Kudos

Now read this

The journey of building a tiny JS library

Excited about what does it take to conceptualize, and publish a library? Learn about project setup, test cases, different JS Modules, Tree shaking, and more in this article. Table of Content GitHub repository Folder structure Installing... Continue →