14 JavaScript Closure Interview Questions (2026)

·10 min read
By ·Updated
javascriptinterview-questionsclosuresfrontendscopefunctions

Closures power React hooks, enable private variables, and appear in nearly every JavaScript technical interview. They're so fundamental that they're built into the language itself—yet most candidates either over-explain them or can't give a practical example.

Table of Contents

  1. Closure Fundamentals Questions
  2. Scope and Lexical Environment Questions
  3. Loop and Async Questions
  4. Practical Applications Questions
  5. Memory and Performance Questions
  6. Quick Reference

Closure Fundamentals Questions

These questions test your core understanding of what closures are and how they work.

What is a closure in JavaScript?

A closure is a function together with the lexical environment in which it was created. Because the function retains access to bindings in its enclosing environments, it can read or update them when it runs later.

The familiar returned-inner-function example shows that access can persist after an outer call returns. That lifetime extension is a consequence of the closure, not part of a requirement that the outer function must already have finished.

How do you demonstrate a closure with a simple example?

The counter example is the classic demonstration of closures in action:

function createCounter() {
  let count = 0;  // This variable is "closed over"
 
  return function() {
    count++;
    return count;
  };
}
 
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

Even though createCounter has finished executing, the returned function still has access to count. Each call increments the same count variable because the inner function maintains a reference to its outer scope.


Scope and Lexical Environment Questions

These questions test your understanding of how closures relate to JavaScript's scoping rules.

How do closures relate to lexical scope?

Closures are a direct result of lexical scoping in JavaScript. Lexical scope means a function's scope is determined by where it's written in the code, not where it's called. Closures leverage this by allowing inner functions to access outer variables based on where they were defined.

The inner function "remembers" its lexical environment even when executed outside of it. This is why a function returned from another function can still access the outer function's variables.

Which scopes can a nested function access?

A nested function resolves identifiers through its current function environment and the chain of enclosing lexical environments. Those environments may come from functions, blocks, catch clauses, modules, and ultimately the global environment. The common "local, outer, global" explanation is a useful starting point, but real code may contain more than three levels.

const globalVar = 'global';
 
function outer() {
  const outerVar = 'outer';
 
  function inner() {
    const innerVar = 'inner';
    console.log(innerVar);  // inner's own scope
    console.log(outerVar);  // outer function's scope
    console.log(globalVar); // global scope
  }
 
  return inner;
}

Loop and Async Questions

These questions test your understanding of the classic closure pitfall with loops and asynchronous code.

Why does the classic for loop with setTimeout print the same number?

When using var in a for loop with setTimeout, all callbacks share the same variable reference. By the time the callbacks execute, the loop has finished and the variable holds its final value.

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 3, 3, 3 (not 0, 1, 2)

This happens because var is function-scoped, not block-scoped. All three callback functions close over the same i variable, and when they finally execute after 1 second, i has already been incremented to 3.

How do you fix the loop closure problem using let?

Using let instead of var creates a new binding for each iteration, so each callback closes over its own copy of the variable.

for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
}
// Output: 0, 1, 2

How do you fix the loop closure problem using an IIFE?

An Immediately Invoked Function Expression (IIFE) captures the current value of i by passing it as a parameter, creating a new scope for each iteration.

for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j);
    }, 1000);
  })(i);
}
// Output: 0, 1, 2

The IIFE creates a new function scope that captures i as j for each iteration. This was the standard solution before let was introduced in ES6.

How do you fix the loop closure problem using forEach?

forEach invokes the callback separately for each element, so the callback parameter is a distinct binding for that invocation. Each timeout therefore closes over the value passed to its own callback call.

[0, 1, 2].forEach(function(i) {
  setTimeout(function() {
    console.log(i);
  }, 1000);
});
// Output: 0, 1, 2

Practical Applications Questions

These questions test your knowledge of real-world closure use cases.

How do you create private variables using closures?

Closures enable data privacy by keeping variables inaccessible from outside the function. You define a variable inside a function and return methods that can access it, but external code cannot access the variable directly.

function createBankAccount(initialBalance) {
  let balance = initialBalance; // Private variable
 
  return {
    deposit: function(amount) {
      balance += amount;
      return balance;
    },
    getBalance: function() {
      return balance;
    }
  };
}
 
const account = createBankAccount(100);
account.deposit(50);     // 150
account.getBalance();    // 150
// account.balance       // undefined - can't access directly!

What is a function factory and how does it use closures?

A function factory is a function that creates and returns other functions, using closures to "bake in" certain values. Each returned function remembers the values from when it was created.

function multiply(x) {
  return function(y) {
    return x * y;
  };
}
 
const double = multiply(2);
const triple = multiply(3);
 
double(5);  // 10
triple(5);  // 15

How do closures work with event handlers?

Event handlers frequently use closures to maintain access to variables from their enclosing scope. This allows you to configure event behavior at setup time while the handler executes later.

function setupButton(buttonId, message) {
  document.getElementById(buttonId).addEventListener('click', function() {
    alert(message); // message is closed over
  });
}

The callback function closes over message, so it remembers the value even though setupButton has already returned when the click happens.


Memory and Performance Questions

These questions test your understanding of the tradeoffs when using closures.

What are the downsides of using closures?

Closures can retain bindings—and objects reachable from those bindings—for as long as the closure itself remains reachable. That may increase memory usage, but it is not automatically a leak. A leak occurs when something long-lived, such as a global registry, timer, cache, or event target, unnecessarily keeps the closure reachable. Debugging can also become harder when behavior depends on mutable state captured across several scopes.

When should you avoid using closures?

Avoid capturing large object graphs when only a small value is needed, especially while creating many long-lived callbacks. A removed DOM subtree is not leaked merely because objects inside it reference one another; modern garbage collectors handle cycles. The risk is an outside, reachable object—such as a listener registry—retaining a callback that in turn retains the detached subtree. In performance-sensitive code, confirm allocation or retention costs with profiling rather than assuming every closure is expensive.

How do you prevent memory leaks with closures?

Remove long-lived event listeners, clear timers and subscriptions, and evict obsolete cache entries when their lifetimes end. Keep captured state narrow: copy the small primitive or identifier you need instead of retaining a large owner object. Setting a reference to null only helps when that reference is the path keeping the closure reachable; weak references are specialized tools and should not replace explicit lifecycle management.


Quick Reference

ConceptKey Points
What is a closure?Function + its lexical environment
Why use closures?Data privacy, factories, callbacks
Loop problem causevar shares scope across iterations
Loop problem fixUse let, IIFE, or forEach
Memory concernClosed variables stay in memory
Lexical scopeScope determined by code location

Official Sources

Frequently Asked Questions

What is a closure in JavaScript?

A closure is a function together with the lexical environment in which it was created. The function can access bindings from enclosing scopes when it later runs, including after an outer function has returned. Returning a nested function makes this easy to demonstrate, but closures are created from lexical scoping and do not require the outer call to have finished.

What are practical uses of closures in JavaScript?

Closures have three main practical uses: 1) Data privacy and encapsulation - creating private variables that cannot be accessed directly from outside. 2) Function factories - creating functions that generate other functions with preset parameters. 3) Event handlers and callbacks - maintaining access to variables in asynchronous code like setTimeout or event listeners.

Why does the classic for loop with setTimeout print the same number?

With var in a for loop, every timeout callback closes over the same function-scoped binding. The loop usually completes before the callbacks run, so they all read its final value. A let declaration creates a fresh binding for each iteration. An IIFE or a forEach callback can also introduce a distinct parameter binding for every value.

What are the downsides of using closures?

A reachable closure can retain bindings and the objects reachable through them, increasing memory use. That is not automatically a leak: it becomes a leak when an unnecessary root, such as a long-lived listener or cache, keeps the closure reachable. Large numbers of closures can also add allocation cost, and deeply nested captured state can make debugging harder. Measure before optimizing.

How do closures relate to lexical scope?

Closures are a direct result of lexical scoping in JavaScript. Lexical scope means a function's scope is determined by where it's written in the code, not where it's called. Closures leverage this by allowing inner functions to access outer variables based on where they were defined in the source code. The inner function 'remembers' its lexical environment even when executed outside of it.

How do you create private variables in JavaScript using closures?

To create private variables using closures, define a variable inside a function and return an object or function that accesses it. The returned object/function has access to the variable through closure, but external code cannot access it directly. For example: function createCounter() { let count = 0; return { increment: function() { return ++count; }, getCount: function() { return count; } }; }. The 'count' variable is private and can only be accessed through the returned methods.

Ready to ace your interview?

Get 550+ interview questions with detailed answers in our comprehensive PDF guides.

View PDF Guides