Learnitweb

Closure in JavaScript

1. Introduction

In this tutorial, we’ll discuss one of the most important concept of JavaScript – Closure. Closure is a popular interview question. Closure is not a special syntax and is not related to a particular class or library. You can think it as a concept. Closure is created as a result of writing code that relies on lexical scope. You may have unintentionally created a Closure without recognizing it.

So, what actually is a Closure?

A Closure is when a function is able to access its lexical scope even when it is executing outside its lexical scope.

Let us see an example of Closure.

function outer() {
  let x = 5;
  function inner() {
    console.log(x);
  }
  return inner;
}
let outerFn = outer();
outerFn(); // 5

In this example, function inner() has a closure over the scope of outer(), because inner() is nested inside of outer(). Let us discuss this in detail.

The function inner() is nested inside outer() function so the function inner() has lexical scope access to the inner scope of outer(). The function outer() returns the function object itself, inner in this case. The statement let outerFn = outer(); executes the outer() function and assigns it to a new reference variable outerFn.

Once the outer() function is executed, we expect whatever is in the inner scope of outer() will be collected or destroyed by the garbage collector. But it does not happen in our example, as we are still able to execute inner() function and the reference is kept alive for later use using outerFn. So when outerFn() is executed, inner() is executed which is able to access the variable x. So a Closure allows the inner() to access its lexical scope even when it is executing outside its lexical scope.

2. A practical example of Closure

A Closure is visible in the module pattern. Traditional function based modules can be used to demonstrate Closure. There are two conditions to create a module pattern:

  • An outer enclosing function which is executed at least once.
  • The outer enclosing function returns at least one inner function nested in outer function so that the inner function has closure over the inner scope of outer function.
function MyModule(msg) {
  function print() {
    console.log(msg);
  }
  return {
    printMg: print,
  };
}
var module = MyModule("Hello World");
module.printMg(); // Hello World

In this code, MyModule() returns print() inside an object which can be used to access inner scope of MyModule.

3. Closure in loop

Before the introduction of let, closure inside loop could lead to unexpected results. With the introduction of let, we can define block level variables which are helpful in clearly defining scope.

for (var i = 1; i <= 5; i++) {
  setTimeout(function print() {
    console.log(i);
  }, i * 1000);
}

The expectation is to print integers 1 to 5 at an interval of 1 second. But the output is 6 printed 5 times at an interval of one second. The reason is callback will always be executed after the loop is executed. The execution of loop results in setting the final value of i as 6.

The solution is to create a scope outside the callback. We can use IFFE for this purpose. The solution using IIFE is:

for (var i = 1; i <= 5; i++) {
  (function (j) {
    setTimeout(function print() {
      console.log(j);
    }, j * 1000);
  })(i);
}

A more clean approach is using let. The let will initialize the variable i at each iteration.

for (let i = 1; i <= 5; i++) {
  setTimeout(function print() {
    console.log(i);
  }, i * 1000);
}