DEV Community

Cover image for ๐Ÿ”ฅ JavaScript Interview Series(13): Closures in Practice โ€” Encapsulation & Privacy
jackma
jackma

Posted on

๐Ÿ”ฅ JavaScript Interview Series(13): Closures in Practice โ€” Encapsulation & Privacy

Closures are one of the most frequently tested topics in JavaScript interviews because they reveal how deeply you understand scope, memory management, and data privacy. In this article, weโ€™ll go through 10 well-structured interview questions, each designed to test your practical understanding of closures โ€” from simple function scope to encapsulating private data.


1. (Interview Question 1) What is a closure in JavaScript?

Focus: Understanding closure fundamentals
Model Answer:
A closure is a function that remembers and accesses variables from its outer scope, even after that outer function has finished executing. Closures are created whenever a function is defined inside another function and the inner function references variables from the outer one.

function outer() {
  let count = 0;
  return function inner() {
    count++;
    console.log(count);
  };
}

const counter = outer();
counter(); // 1
counter(); // 2
Enter fullscreen mode Exit fullscreen mode

Here, the inner function โ€œremembersโ€ count even after outer() has returned.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. Can closures access variables from multiple outer scopes?
  2. How does garbage collection handle variables captured by closures?
  3. Whatโ€™s the difference between a closure and a regular function scope?

2. (Interview Question 2) How can closures be used to implement data privacy?

Focus: Encapsulation and private state
Model Answer:
Closures can hide variables inside a functionโ€™s scope so theyโ€™re not accessible from the outside. This mimics private variables, a pattern often used before ES6 class fields.

function createBankAccount(initialBalance) {
  let balance = initialBalance;
  return {
    deposit(amount) { balance += amount; },
    withdraw(amount) { balance -= amount; },
    getBalance() { return balance; }
  };
}
Enter fullscreen mode Exit fullscreen mode

Here, balance is private and cannot be modified directly.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How would you modify this example using ES6 private fields (#balance)?
  2. What are the pros and cons of using closures for data privacy?
  3. Could this approach lead to memory leaks? Why or why not?

3. (Interview Question 3) What will this code output and why?

Focus: Loop variable capture in closures

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
Enter fullscreen mode Exit fullscreen mode

Model Answer:
It will print 3, 3, 3 because var has function scope, not block scope. By the time the callback executes, the loop has already completed and i equals 3.

To fix it:

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
Enter fullscreen mode Exit fullscreen mode

Now it prints 0, 1, 2 because let creates a new block-scoped variable on each iteration.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How could you fix this without using let?
  2. What happens if you wrap setTimeout in an IIFE?
  3. Why do closures behave differently with var and let?

4. (Interview Question 4) Explain how closures enable function factories.

Focus: Functional programming pattern
Model Answer:
Closures allow a function to โ€œrememberโ€ parameters and create specialized versions of other functions.

function multiplier(factor) {
  return function (num) {
    return num * factor;
  };
}

const double = multiplier(2);
const triple = multiplier(3);
Enter fullscreen mode Exit fullscreen mode

double(5) โ†’ 10, triple(5) โ†’ 15.
Here, each function retains its own closure with factor.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. What are real-world use cases for this pattern?
  2. How do closures help with partial application or currying?
  3. Could this lead to performance overhead in large applications?

5. (Interview Question 5) How do closures interact with asynchronous code?

Focus: Event loop and closure persistence
Model Answer:
Closures retain variables even when the surrounding function has finished, which is crucial for async operations like setTimeout or fetch.

function delayedLog(message, delay) {
  setTimeout(() => console.log(message), delay);
}
delayedLog("Hello", 1000);
Enter fullscreen mode Exit fullscreen mode

Here, the closure retains access to message until the timeout executes.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How does JavaScriptโ€™s event loop handle closures in async operations?
  2. Can closures cause unexpected memory retention in async callbacks?
  3. How would you debug closure-related async bugs?

6. (Interview Question 6) How can closures help with memoization?

Focus: Optimization using closure-based caching
Model Answer:
Closures can store computed results to avoid redundant work.

function memoizedAdd() {
  const cache = {};
  return function (x, y) {
    const key = `${x},${y}`;
    if (cache[key]) return cache[key];
    return (cache[key] = x + y);
  };
}
Enter fullscreen mode Exit fullscreen mode

Here, the closure keeps the cache alive between calls, optimizing performance.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. What are potential drawbacks of using closures for caching?
  2. How would you implement cache eviction?
  3. How could you generalize this for any pure function?

7. (Interview Question 7) What happens if a closure references a variable that changes later?

Focus: Reference vs. value in closures
Model Answer:
Closures capture references to variables, not their values at the moment of creation.

let counter = 0;
function makeLogger() {
  return function () {
    console.log(counter);
  };
}
const log = makeLogger();
counter = 5;
log(); // 5
Enter fullscreen mode Exit fullscreen mode

Because counter is captured by reference, its updated value is logged.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How can you capture the value at creation instead of a reference?
  2. What happens if the variable is an object?
  3. Can closures capture constants differently?

8. (Interview Question 8) Can closures cause memory leaks?

Focus: Memory management and lifecycle
Model Answer:
Yes, if closures unintentionally retain references to large or unused objects, the garbage collector wonโ€™t reclaim them.

Example:

function createLeak() {
  const bigData = new Array(1000000).fill('*');
  return function () {
    console.log(bigData.length);
  };
}
Enter fullscreen mode Exit fullscreen mode

As long as the returned function exists, bigData remains in memory.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How can you prevent memory leaks caused by closures?
  2. When does garbage collection reclaim closed-over variables?
  3. How do weak references relate to this issue?

9. (Interview Question 9) Whatโ€™s the difference between closures and immediately invoked functions (IIFEs)?

Focus: Scoping and encapsulation comparison
Model Answer:
An IIFE runs immediately to create a private scope, while a closure persists to maintain access to variables after execution.

(function() {
  let privateData = 42;
  console.log(privateData);
})(); // Executes once

function makeClosure() {
  let privateData = 42;
  return () => console.log(privateData);
}
const fn = makeClosure();
fn(); // Retains access
Enter fullscreen mode Exit fullscreen mode

Closures preserve state, IIFEs isolate execution.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. Can IIFEs return closures?
  2. How did IIFEs influence early JavaScript module design?
  3. How do ES modules make IIFEs less necessary today?

10. (Interview Question 10) How do closures enable modular design in JavaScript?

Focus: Module pattern and architecture
Model Answer:
Closures are the foundation of the module pattern, allowing functions to expose public APIs while keeping implementation details private.

const CounterModule = (function() {
  let count = 0;
  function increment() { count++; }
  function getCount() { return count; }
  return { increment, getCount };
})();
Enter fullscreen mode Exit fullscreen mode

count is private; only increment and getCount are exposed.

Possible Follow-up Questions: ๐Ÿ‘‰ (Want to test your skills? Try a Mock Interview โ€” each question comes with real-time voice insights)

  1. How does this compare with ES6 modules?
  2. What happens if multiple modules share the same closure?
  3. Can closures simulate class inheritance patterns?

Top comments (0)