What is really so special about JavaScript Closure?

JavaScript Closure

“Can you explain to me what a closure is?”

If you are a full-stack, frontend, backend, or do anything with JavaScript, you must hear of the term closure. If you are interviewing for a full-stack software engineer role, you might encounter a question asking you to explain what a closure is.

The questions stated above are easy to answer - just memorized the definition of Closure and gave a couple of examples.

The problem with this terminology is not about understanding the definition of ClosureClosure, but to understand why you might want to use it in your project.

Before we dive deep into how sturdy a closure is in JavaScript, let’s understand what a closure is.

Closure is a function that has access to its ‘outer’ scope that it is defined. Therefore, it can access the value in the outer scope, even if the enclosing function is terminated.

In a scala, a closure is equivalent to currying.

Take this as an example:

function takeOne() {
  let i = 0;
  return function incrementFunction() {
    return i++;
  }
}

The code above represents a function returning another function. However, after you evoke takeOne and get incrementFunction, incrementFunction can the local variable of takeOne even though takeOne has already terminated.

Benefit of Closure

The first benefit of ClosureClosure is to preserve local variables within the scope. Since javaScript is a first-class function, developers often encounter name clashing, that will cause some unexpected output. Using ClosureClosure can help preserve the namespace in that scope, private variable. You can see this a lot in the past of jQuery code, where one defines a click method.

$(function() {
  var selections = []
  $(".something").click(function() { // this closure has access to the outer variable selections
    selections.push("something") // this are able to get the outer function selections
  })
})

While this is indeed one of the use-cases of ClosureClosure, it might leave you thinking, “Is this really what the real purpose of closure?” You might still question the statement as to what is the general use case of ClosureClosure might be.

The second benefit, which is more of a general use case, is that it is useful in an asynchronous environment.

Imagine if you need to loop through a value in an array:

for(var i = 0 ; i< 3; i++) {
  setTimeout(() => console.log(i), 3000)
}

What will the output of this program?

It prints 3 three times. Since, setTimeout is asynchronous, by the time the loop finishes, the outer scope i has also changed to 3, and the subsequent call to setTimeout during the loop triggers the callback and print 3.

How would you solve this problem?

There are many ways, including using an ES6 syntax let instead of var to define its scope at the block level and solve the issue. However, if they want you to solve this issue without using any ES6 feature, ClosureClosure is your answer.

function printSomething(i) {
  setTimeout(() => console.log(i), 3000)
}

for(var i = 0; i<3; i++) {
  printSomething(i)
}

By just creating another outer function outside of setTimeout, you are defining a closure. The i value is preserved even after printSomething is terminated. The callback then prints 0 1 2 to the console.

That is the reason why ClosureClosure is powerful javaScript feature. You can use ClosureClosure to preserve the scope of the outer variable in an asynchronous environment.

Another Example One

Let’s imagine another example where you need to create a function that needs to call 3rd party API and aggregate the result and return it to the caller.

function getAPI(cb) {
    setTimeout(() => cb("a"), 3000)
}

function getAPIB(cb) {
    setTimeout(() => cb("b"), 2000)
}

function getAPIC(cb) {
    setTimeout(() => cb("c"), 1000)
}

function aggregateValue() {
  var aggregateData = []
  
  // your implementation here
}

Before you continue reading, about the solution, pause for a second and think about how you can solve this without promises, async/await.

We can leverage the power of ClosureClosure to preserve the scope of the function and to stop aggregateValue to return early by using callbacks.

Since getAPI, getAPIB, getAPIC all uses a callback function, you can create a callback function that increments the number of count of API called so far. Once the number of API called so far counter exceed 2, call the return callback value.

function aggregateValue(cb) {
  var aggregateData = []
  var numberAPICalledSoFar = 0
  
  function callback(value) {
    aggregateData = [...aggregateData, value]
    if(numberAPICalledSoFar < 2) {
      numberAPICalledSoFar++;
    }else {
      cb(aggregateData)
    }
  }
  getAPI(callback)
  getAPIB(callback)
  getAPIC(callback)
}

The above code leverages, again, the power of ClosureClosure, to preserve the local variable of the enclosing function when it is triggered. As getAPI finishes its called and evoke the callback function, the callback function access the outer scope aggregateValue to increment the number of counts that the API finishes executing. The aggregateData then return with a callback from the outer aggregateValue function that needs all the aggregate data from all the 3rd party API.

Run this function:

aggregateValue((ans) => ans.foreach(console.log))

Takeaway

  • Although ClosureClosure is taught in the first class of javaScript language, ClosureClosure is a more “advanced” feature of javaScript.
  • The problem that is hard about ClosureClosure is not its concept, but its benefit, and why you, as a JavaScript developer, needs to understand its use case.
  • The general use cases of ClosureClosure lies within solving computation in an asynchronous environment.

All the source code in this tutorial is here.

Like this Article?

Sign up for my newsletter to get notified for new articles!


Related Posts

5 Anti Pattern for Writing Code in a Functional Programming Language

No 1. Nested Asynchronous Function

Use This Mantra to Decide whether You Want to go to Big Tech or a Startup

If you want to Go Deep, Go for Big Tech. If you want to Go Wide, Go for Startup.

Why Do Functional Programmers Prefer For-Comprehension Over Imperative Code Block

Short Answer - Readability

Does Seniority Title Really Matters in Tech

The answer is always - depends

An Interview Question that Truly Tests your Experience as a Software Engineer

questions that is not based on Leetcode