DEV Community

Cover image for 20 ๐—๐—ฎ๐˜ƒ๐—ฎ๐—ฆ๐—ฐ๐—ฟ๐—ถ๐—ฝ๐˜ ๐—–๐—น๐—ผ๐˜€๐˜‚๐—ฟ๐—ฒ ๐—œ๐—ป๐˜๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฒ๐˜„ ๐—ค๐˜‚๐—ฒ๐˜€๐˜๐—ถ๐—ผ๐—ป๐˜€ ๐˜๐—ผ ๐—ก๐—ฎ๐—ถ๐—น ๐—ฌ๐—ผ๐˜‚๐—ฟ ๐—ก๐—ฒ๐˜…๐˜ ๐—ง๐—ฒ๐—ฐ๐—ต ๐—ฅ๐—ผ๐—น๐—ฒ๐Ÿš€
Hasan Rifat
Hasan Rifat

Posted on • Edited on

20 ๐—๐—ฎ๐˜ƒ๐—ฎ๐—ฆ๐—ฐ๐—ฟ๐—ถ๐—ฝ๐˜ ๐—–๐—น๐—ผ๐˜€๐˜‚๐—ฟ๐—ฒ ๐—œ๐—ป๐˜๐—ฒ๐—ฟ๐˜ƒ๐—ถ๐—ฒ๐˜„ ๐—ค๐˜‚๐—ฒ๐˜€๐˜๐—ถ๐—ผ๐—ป๐˜€ ๐˜๐—ผ ๐—ก๐—ฎ๐—ถ๐—น ๐—ฌ๐—ผ๐˜‚๐—ฟ ๐—ก๐—ฒ๐˜…๐˜ ๐—ง๐—ฒ๐—ฐ๐—ต ๐—ฅ๐—ผ๐—น๐—ฒ๐Ÿš€

(** to elevate you from junior to senior role๐Ÿ˜Ž**)

โœ…How does a closure work?

โœ…Can you write a function that creates a private variable using a closure?

โœ…Explain why the following code outputs 3 three times, and how would you fix it?
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}

โœ…How can closures be used to create a memoization function?

โœ…Write a function that generates unique IDs using a closure.

โœ…What are the potential downsides of using closures?

โœ…Can you use a closure to throttle a function?

โœ…How would you use a closure to create a module pattern?

โœ…How does a closure interact with event listeners?

โœ…Explain the difference between a closure and a regular function.

โœ…How can you use a closure to create a function that only runs once?

โœ…Write a closure-based function to maintain a history of values.

โœ…How would you use a closure to create a factory function for generating customized greetings?

โœ…Can you explain how closures are used in currying, and provide an example?

โœ…How can closures cause memory leaks, and how would you prevent them?

โœ…Write a closure to limit the number of times a function can be called.

โœ…How would you use a closure to create a toggle function?

โœ…Can you create a closure to simulate a class-like structure without using the class keyword?

โœ…How do closures interact with asynchronous code, like Promises?

โœ…Write a closure-based function to debounce user input.

Now let's explain all of the given question one by one๐Ÿ‘‹

๐Ÿ‘‰1.What is a closure in JavaScript, and how does it work?
โœ๏ธA closure is a function that remembers the variables in its outer scope, even when executed outside that scope. Itโ€™s created due to JavaScriptโ€™s lexical scoping, where a functionโ€™s scope is defined by where itโ€™s written in the code. When an inner function references outer variables, JavaScript preserves those variables in memory, forming a closure

function outer() {
  let count = 0;
  return function inner() {
    count++;
    return count;
  };
}
Enter fullscreen mode Exit fullscreen mode
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
Enter fullscreen mode Exit fullscreen mode

The inner function closes over count, so even after outer finishes, count persists in memory. Each call to counter() increments it, showing how closures maintain state. This is a core concept for interviews, as it tests your grasp of scope and memory.

๐Ÿ‘‰2.Can you write a function that creates a private variable using a closure?
โœ๏ธClosures are perfect for encapsulation, letting you create private variables that canโ€™t be accessed directly. By defining a variable in an outer function and exposing methods to interact with it, you ensure data privacy.

function createPerson() {
  let name = "Alice"; // Private variable
  return {
    getName: function() {
      return name;
    },
    setName: function(newName) {
      name = newName;
    }
  };
}
Enter fullscreen mode Exit fullscreen mode
const person = createPerson();
console.log(person.getName()); // "Alice"
person.setName("Bob");
console.log(person.getName()); // "Bob"
console.log(person.name); // undefined
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
name is private because itโ€™s only accessible through getName and setName. This pattern mimics private properties in OOP, a skill senior developers use to write secure, modular code javaScript

๐Ÿ‘‰3.Explain why the following code outputs 3 three times, and how would you fix it?
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}

โœ๏ธThis outputs 3-three times because var is function-scoped, not block-scoped. The setTimeout callbacks all reference the same i, which is 3 by the time the loop finishes and the callbacks run.
Fix using let:

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

Fix using IIFE (if using var):

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

Explanation๐Ÿ‘€:
Using let creates a new i for each iteration, so each closure captures the correct value. An IIFE creates a new scope per iteration, passing i as j. This question tests your understanding of scoping and closures in loopsโ€”a common interview gotcha.

๐Ÿ‘‰4.How can closures be used to create a memoization function?
โœ๏ธMemoization optimizes functions by caching results for repeated inputs. A closure can maintain a private cache, storing computed values for reuse.

function memoize(fn) {
  let cache = {};
  return function(...args) {
    let key = JSON.stringify(args);
    if (key in cache) {
      return cache[key];
    }
    cache[key] = fn(...args);
    return cache[key];
  };
}
Enter fullscreen mode Exit fullscreen mode
function slowFib(n) {
  if (n <= 1) return n;
  return slowFib(n - 1) + slowFib(n - 2);
}
Enter fullscreen mode Exit fullscreen mode
const memoFib = memoize(slowFib);
console.log(memoFib(10)); // 55 (computed)
console.log(memoFib(10)); // 55 (cached)
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over cache stores results, making subsequent calls faster. Senior developers use memoization to optimize performance, and this question tests that mindset.

๐Ÿ‘‰5.Write a function that generates unique IDs using a closure.
โœ๏ธA closure can maintain a private counter to generate unique IDs, ensuring no external interference.

function createIdGenerator() {
  let id = 0;
  return function() {
    return id++;
  };
}
Enter fullscreen mode Exit fullscreen mode
const generateId = createIdGenerator();
console.log(generateId()); // 0
console.log(generateId()); // 1
console.log(generateId()); // 2
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over id keeps it private and increments it per call. This is useful for generating keys in apps, showing practical closure application.

๐Ÿ‘‰6.What are the potential downsides of using closures?
While powerful, closures have trade-offs:
Memory Usage: Closed-over variables stay in memory until the closure is garbage-collected, potentially causing leaks if mismanaged.
Performance: Creating many closures (e.g., in loops) can increase memory and processing overhead.
Complexity: Overuse can make code harder to debug or maintain.

function leaky() {
  let bigData = new Array(1000000).fill("data");
  return () => console.log(bigData.length);
}
Enter fullscreen mode Exit fullscreen mode

Here, bigData persists unnecessarily, consuming memory.

Explanation๐Ÿ‘€:
Senior developers balance closures with cleanup (like nullifying references or removing event listeners) to avoid issues, a key interview discussion point.

๐Ÿ‘‰7.Can you use a closure to throttle a function?
โœ๏ธThrottling limits how often a function runs, useful for performance. A closure can track the last execution time.

function throttle(fn, delay) {
  let lastCall = 0;
  return function(...args) {
    let now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      return fn(...args);
    }
  };
}
Enter fullscreen mode Exit fullscreen mode
const log = () => console.log("Throttled!");
const throttledLog = throttle(log, 1000);
setInterval(throttledLog, 200);
// Logs every ~1000ms
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over lastCall ensures the function runs only after delay has passed. This is common in UI optimizations, showing senior-level thinking.

๐Ÿ‘‰8.How would you use a closure to create a module pattern?
โœ๏ธThe module pattern uses a closure to create private and public members, mimicking encapsulation.

const myModule = (function() {
  let privateData = "Secret";
  function privateMethod() {
    return `Accessing ${privateData}`;
  }

  return {
    publicMethod: function() {
      return privateMethod();
    },
    publicData: "Public info"
  };
})();
Enter fullscreen mode Exit fullscreen mode
console.log(myModule.publicMethod()); // "Accessing Secret"
console.log(myModule.privateData); // undefined
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The IIFE creates a closure, keeping privateData and privateMethod hidden. This pattern is used in libraries, testing your modular design skills.

๐Ÿ‘‰9.How does a closure interact with event listeners?
โœ๏ธClosures in event listeners retain outer variables, enabling dynamic behavior in response to events.

function setupButton() {
  let count = 0;
  const button = document.createElement("button");
  button.textContent = "Click me";
  button.addEventListener("click", () => {
    count++;
    console.log(`Clicked ${count} times`);
  });
  document.body.appendChild(button);
}
Enter fullscreen mode Exit fullscreen mode

setupButton();
Explane listenerโ€™s closure over count tracks clicks. Senior developers know to remove listeners to prevent leaks, a nuance interviewers may probe.

๐Ÿ‘‰10. Explain the difference between a closure and a regular function.
โœ๏ธA regular function accesses its own variables and parameters but loses outer variables after their scope ends. A closure retains access to its outer scopeโ€™s variables indefinitely.

// Regular function
function add(a, b) {
  return a + b;
}
Enter fullscreen mode Exit fullscreen mode
console.log(add(2, 3)); // 5
Enter fullscreen mode Exit fullscreen mode
// Closure
function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}
Enter fullscreen mode Exit fullscreen mode
const addFive = makeAdder(5);
console.log(addFive(3)); // 8
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure in makeAdder remembers x, unlike add, which doesnโ€™t persist state. This tests your ability to articulate core differences.

๐Ÿ‘‰11.How can you use a closure to create a function that only runs once?
โœ๏ธA closure can track execution with a flag, ensuring a function runs only once.

function createOnceRunner() {
  let hasRun = false;
  return function() {
    if (!hasRun) {
      hasRun = true;
      console.log("This runs only once!");
    } else {
      console.log("Already ran!");
    }
  };
}
Enter fullscreen mode Exit fullscreen mode
const runOnce = createOnceRunner();
runOnce(); // "This runs only once!"
runOnce(); // "Already ran!"
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over hasRun controls execution, useful for initialization tasks in app.

๐Ÿ‘‰12.Write a closure-based function to maintain a history of values.
โœ๏ธA closure can store an array to track values over time, keeping it private.

function createHistoryTracker() {
  let history = [];
  return function(value) {
    history.push(value);
    return history;
  };
}
Enter fullscreen mode Exit fullscreen mode
const tracker = createHistoryTracker();
console.log(tracker("Step 1")); // ["Step 1"]
console.log(tracker("Step 2")); // ["Step 1", "Step 2"]

Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over history preserves the state, a pattern for logging or undo features.

๐Ÿ‘‰13.How would you use a closure to create a factory function for generating customized greetings?
โœ๏ธA closure can customize functions by retaining configuration parameters.

function createGreeter(greeting) {
  return function(name) {
    return `${greeting}, ${name}!`;
  };
}
Enter fullscreen mode Exit fullscreen mode
const sayHello = createGreeter("Hello");
console.log(sayHello("Alice")); // "Hello, Alice!"
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
Each returned function remembers its greeting, showing how closures enable reusable patterns.

๐Ÿ‘‰14.Can you explain how closures are used in currying, and provide an example?
โœ๏ธCurrying splits a multi-argument function into single-argument functions, using closures to hold intermediate values.

function curryAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}
Enter fullscreen mode Exit fullscreen mode
console.log(curryAdd(1)(2)(3)); // 6
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
Each inner function closes over outer parameters, enabling functional flexibilityโ€”a senior-level skill.

๐Ÿ‘‰15.How can closures cause memory leaks, and how would you prevent them?
โœ๏ธClosures keep variables in memory, which can lead to leaks if not cleaned up, especially with large objects or event listeners.

function setupLeak() {
  let heavyData = new Array(1000000).fill("data");
  document.getElementById("btn").addEventListener("click", () => {
    console.log(heavyData.length);
  });
}
Enter fullscreen mode Exit fullscreen mode

Prevention:

  • Remove listeners: removeEventListener.
  • Nullify references: heavyData = null.
  • Use WeakMap for temporary data. Explanation๐Ÿ‘€: Senior developers proactively manage memory, a key interview topic.

๐Ÿ‘‰16.Write a closure to limit the number of times a function can be called.
โœ๏ธA closure can track call counts to enforce a limit.

function limitCalls(fn, maxCalls) {
  let calls = 0;
  return function(...args) {
    if (calls < maxCalls) {
      calls++;
      return fn(...args);
    }
    return "Max calls reached!";
  };
}
Enter fullscreen mode Exit fullscreen mode
const limitedLog = limitCalls(console.log, 2);
limitedLog("First"); // "First"
limitedLog("Second"); // "Second"
limitedLog("Third"); // "Max calls reached!"
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over calls ensures controlled execution, useful for APIs or trials.

๐Ÿ‘‰17.How would you use a closure to create a toggle function?
โœ๏ธA closure can maintain a boolean state for toggling.

function createToggle() {
  let isOn = false;
  return function() {
    isOn = !isOn;
    return isOn ? "On" : "Off";
  };
}
Enter fullscreen mode Exit fullscreen mode
const toggle = createToggle();
console.log(toggle()); // "On"
console.log(toggle()); // "Off"
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over isOn tracks state privately, ideal for UI controls.

๐Ÿ‘‰18.Can you create a closure to simulate a class-like structure without using the class keyword?
โœ๏ธA closure can encapsulate data and methods, mimicking a class.

function createStudent(name) {
  let score = 0;
  return {
    getName: () => name,
    addScore: (points) => {
      score += points;
      return score;
    },
    getScore: () => score
  };
}
Enter fullscreen mode Exit fullscreen mode
const student = createStudent("Alice");
console.log(student.addScore(10)); // 10
console.log(student.getScore()); // 10
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure hides score, offering controlled accessโ€”a pre-ES6 class alternative.

๐Ÿ‘‰19.How do closures interact with asynchronous code, like Promises?
โœ๏ธClosures let async operations access outer variables after delays.

function fetchWithContext(id) {
  let context = `Request-${id}`;
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`${context}: Done`);
    }, 1000);
  });
}
Enter fullscreen mode Exit fullscreen mode
fetchWithContext(1).then(console.log); // "Request-1: Done"
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The Promiseโ€™s closure over context ensures correct data, common in API calls

๐Ÿ‘‰20.Write a closure-based function to debounce user input.
โœ๏ธDebouncing delays function execution until rapid calls stop, using a closure to track timeouts.

function debounce(fn, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}
Enter fullscreen mode Exit fullscreen mode
const logInput = debounce((value) => console.log(`Input: ${value}`), 500);
logInput("a");
logInput("b");
setTimeout(() => logInput("c"), 200);
// Logs "Input: c" after ~500ms
Enter fullscreen mode Exit fullscreen mode

Explanation๐Ÿ‘€:
The closure over timeoutId ensures only the last call runs, optimizing search or resize handlers.

Which of these closure questions challenged you the most? Or do you have a closure tip thatโ€™s helped you level up? Drop a comment belowโ€”Iโ€™d love to hear your thoughts and keep the conversation going!

Top comments (2)

Collapse
 
nevodavid profile image
Nevo David

Amazing article! It clearly explains closures in JavaScript and their practical uses. Very helpful!

Collapse
 
hasan_rifat profile image
Hasan Rifat

Thanks man๐Ÿ‘‹