03 / 10

What is the problem of creating closures inside loops?

When closures are created inside loops, they capture the variables from the surrounding environment, including the loop's variables. Since closures retain references to these variables by capturing the entire environment, they don't actually store the current value of those variables; instead, they maintain references to the variables themselves. As a result, if the loop continues to execute and update its variables, the closures created inside the loop will use the updated values, which might not be the expected behaviour.

In this example, you might expect the output to be `0`, `1`, and `2` printed each after a second. However, the actual output will be `3`, `3`, and `3`, all printed after one second. This is because the closures created by the `setTimeout` function capture the variable `i`, and by the time the closures execute (after one second), the loop has already been completed and `i` has the final value of `3`.
What is the issue:
  1. 1

    Explanation: When the loop runs, the setTimeout callbacks are scheduled, and the closures inside them capture the reference to the variable i. The closures don't have their own copy of i; they share a reference to the same i variable. As the loop iterates, the value of i changes until it reaches 3. Since the closures capture the reference to i, when they finally execute, they use the latest value of i, which is 3.

  2. 2

    Solution: To avoid this problem, you can create a new scope inside each iteration of the loop to capture the current value of i. This can be achieved using an Immediately Invoked Function Expression (IIFE) or by using the let keyword, which has block-level scope:

javascript
Difficulty: 6/10
Topics: lexical scope, memory management, event loop

Scenario Questions

0-2 years experience
  1. 1

    Imagine you're building a simple tab component. You loop through 5 button elements to attach a click handler that alerts the button's index. When you click any button, it always alerts '5'. Why is this happening, and how would you fix it?

  2. 2

    We have a loop that's supposed to print numbers 0 through 4 with a one-second delay using setTimeout. Instead, it prints '5' five times. Walk me through why the engine behaves this way and how we can get it to print 0, 1, 2, 3, 4.

2-5 years experience
  1. 1

    We have a React component that renders a list of active file uploads. Inside a loop, we set up a WebSocket listener for each file that references the loop's local state. Users are reporting that progress updates are updating the wrong files or showing stale data. How would you debug this scope issue?

  2. 2

    You're writing a Node.js script that processes thousands of database rows in a loop. Inside the loop, you define an inline helper function to format each row before saving. The script is running out of memory on large datasets. Why is defining that function inside the loop problematic for the garbage collector, and how would you refactor it?

5-8 years experience
  1. 1

    We are building a real-time data visualization dashboard that updates 60 times a second. A junior developer wrote a loop that instantiates event handlers inside the render loop, causing massive GC thrashing and frame drops. How do you refactor this to avoid allocation overhead while still maintaining access to the correct contextual data?

  2. 2

    You are refactoring a legacy ES5 SDK where block scoping ('let'/'const') isn't fully supported or trusted due to target environments, and we cannot use transpilers. We have complex asynchronous initialization loops that are leaking memory and sharing state. How would you design a clean, memory-efficient pattern using IIFEs or factory functions to isolate scope safely?

8+ years experience
  1. 1

    In a large-scale micro-frontend architecture, we've noticed memory leaks traced back to dynamically generated event handlers inside loops within legacy shared libraries. How would you address this systematically? What linting rules, architectural boundaries, or custom compiler transforms would you introduce to prevent this across 50+ engineering teams?

  2. 2

    We are designing a high-throughput serverless execution environment where cold starts and memory footprints are critical. How does the V8 engine handle closure allocation and context saving inside loops under the hood, and what architectural guidelines would you establish for our team to minimize heap allocation overhead in our core request-routing loops?

Follow-up Questions

  • How does the V8 engine optimize block-scoped variables in loops under the hood?
  • What is the memory footprint difference between an inline closure and a partially applied function defined outside the loop?
  • How would you write a performance benchmark to measure the GC impact of loop closures in Node.js?