Day 1 - 100 JS Interview Questions : Closures

Interviewer : What are closures, and how do they work in JavaScript?

Imagine a backpack you carry to school..

  • The backpack is like the outer function, which holds supplies (variables).

  • Inside the backpack, there’s a notebook (closure) that keeps notes for your math class.

  • Even when you leave school and go home, the notebook in your backpack still has the math notes, allowing you to study later.

Similarly, in JavaScript, a closure carries the variables it needs (like the math notes) even after the outer function (school) is finished.

At its core, a closure is a feature in JavaScript that allows a function to "remember" the environment in which it was created, even after the outer function has finished executing. This means that a closure gives access to an outer function's scope from within an inner function.


function outerFunction() {
  let outerVariable = "I'm from the outer function!";

  function innerFunction() {
    console.log(outerVariable); // Accessing outerVariable from the inner function
  }

  return innerFunction;
}

const myClosure = outerFunction(); // outerFunction returns innerFunction
myClosure(); // Logs: "I'm from the outer function!"

Here, outerFunction creates a local variable outerVariable. innerFunction is defined inside outerFunction and accesses outerVariable. The fun thing is even after outerFunction has finished running, innerFunction still remembers and can access outerVariable. That’s a closure!


Step 1 : Function Creation - When outerFunction is called, the JavaScript engine creates a scope for it. This scope contains variables like outerVariable.

Step 2 : Returning the Inner Function - outerFunction returns innerFunction, but importantly, the scope (and variables) of outerFunction are not discarded when outerFunction finishes.

Step 3 : Closure Formation - When innerFunction is called later (via myClosure()), it has access to the scope of outerFunction, even though outerFunction has already completed. This is because the JavaScript engine keeps a reference to the outer scope in memory due to the closure.


What does this code print?

function outerFunction() {
  let counter = 0;

  return function innerFunction() {
    console.log(counter);
    counter++;
  };
}

const func1 = outerFunction();
const func2 = outerFunction();

func1(); // ? __
func1(); // ? __
func2(); // ? __
func2(); // ? __

Take a guess…

  • func1 and func2 create independent closures with separate counter variables.

  • Output:

    • func1(): 0

    • func1(): 1

    • func2(): 0

    • func2(): 1

💡
Each call to outerFunction creates a new closure with its own counter.

Lets have a look at one typical use case regarding this: Memory Management

Closures can sometimes hold onto memory unnecessarily, causing memory leaks. For example:

function largeClosure() {
  const bigArray = new Array(1000000).fill("data");

  return function smallFunction() {
    console.log("Don't Look at Me!!, I don't use bigArray");
  };
}

const closure = largeClosure();

Here, bigArray remains in memory because of the closure, even though smallFunction doesn’t use it. To avoid this, minimize unnecessary variables in closures.


Another common question to be anticipated is tell me about real life applications of Closures. Basically a question related to real life applications can be rare, yet it is best to be prepare for them at any stage.

One of the most common application of closures is Creating Private Variables. In JavaScript, closures help simulate private variables, as JavaScript does not have native private variables like some other languages.

function bankAccount(initialBalance) {
  let balance = initialBalance;

  return {
    deposit: function (amount) {
      balance += amount;
      console.log(`Deposited: $${amount}. Balance: $${balance}`);
    },
    withdraw: function (amount) {
      if (amount > balance) {
        console.log("Insufficient funds!");
      } else {
        balance -= amount;
        console.log(`Withdrew: $${amount}. Balance: $${balance}`);
      }
    },
  };
}

const account = bankAccount(100);
account.deposit(50);  // Deposited: $50. Balance: $150
account.withdraw(30); // Withdrew: $30. Balance: $120

Take a note that the balance variable is hidden from direct access. Only the returned deposit and withdraw methods can interact with balance.

Secondly, Closures are essential in handling events.

function attachEventHandlers() {
  for (let i = 1; i <= 3; i++) {
    document.getElementById(`button${i}`).addEventListener("click", function () {
      console.log(`Button ${i} clicked!`);
    });
  }
}

attachEventHandlers();

Here, closures let each button "remember" its unique i value even after the loop finishes.


Predict the output of the following code:

function createFunctions() {
  let arr = [];
  for (var i = 0; i < 3; i++) {
    arr.push(function () {
      return i;
    });
  }
  return arr;
}

const funcs = createFunctions();
console.log(funcs[0]()); // __ ?
console.log(funcs[1]()); // __ ?
console.log(funcs[2]()); // __ ?

Create a Timer: Write a function that returns another function to count seconds from 0. Each time the returned function is called, it should increment the count.

Private Counter: Implement a counter function where you cannot directly access or modify the count variable, except through specific methods.


A closure is like carrying a "notebook" (inner function) with notes (variables) from the classroom (outer function), even when you're home (the outer function has ended). Closures are powerful for protecting data from outside interference and creating flexible, reusable code for tasks like counters, event listeners, and encapsulation. They also come with responsibilities, like avoiding unnecessary memory usage.

Subscribe to our newsletter

Read articles from JavaScript Interview Prep directly inside your inbox. Subscribe to the newsletter, and don't miss out.