CodeBlanks Logo Icon CodeBlanks

Closure pitfalls

JavaScript course · Intermediate · Lesson 17/134

Learn a common mistake creating closures in loops and how to fix it.

Closure pitfall

A classic closure pitfall: creating closures in a loop with var. Because var is function-scoped, all closures capture the same i. When the loop ends, i keeps its final value; calling any closure returns that same final value.

This is called a stale reference: the closure thinks it points to an old value, but it actually always sees the last one.

The fix with let

The fix: use let instead of var. let is block-scoped; each iteration creates a fresh binding. Each closure captures its own i and returns the right index when called.

This is why modern JavaScript always prefers let in loops. Even if you see var in old code, use let when writing new code; this simple change protects you from sneaky bugs.

Exercise Fix the closure in a loop

Define a function called makeFunctions(). Inside it create an empty fns array. With a for (let i = 0; i < 3; i++) loop, do fns.push(function() { return i; }) on each iteration. Finally return fns. Call it and log all three elements to the console.

Show example solution
function makeFunctions() {
  const fns = [];
  for (let i = 0; i < 3; i++) {
    fns.push(function() {
      return i;
    });
  }
  return fns;
}

const fns = makeFunctions();
console.log(fns[0]());
console.log(fns[1]());
console.log(fns[2]());

Test yourself

  1. Match the pitfall terms

    loop capture
    Closure capturing a loop variable
    stale reference
    Reference pointing to old value
    let in loop
    Fresh scope per iteration
  2. What happens when creating closures in a loop with var?

    • Each closure captures its own index
    • All closures capture the loop's final index
    • The loop never runs
    • Closures reset automatically
    Show answer

    All closures capture the loop's final index — Correct. var is function-scoped; all closures share the same i, which keeps its final value after the loop.

  3. Pick the correct loop

    // Wrong: every function captures the same i
    const wrong = [];
    for (var i = 0; i < 3; i++) {
      wrong.push(() => i);
    }
    
    // Right: each iteration binds a fresh i
    const right = [];
    for (____) {
      right.push(() => i);
    }
    Show answer
    // Wrong: every function captures the same i
    const wrong = [];
    for (var i = 0; i < 3; i++) {
      wrong.push(() => i);
    }
    
    // Right: each iteration binds a fresh i
    const right = [];
    for (let i = 0; i < 3; i++) {
      right.push(() => i);
    }

All lessons in Intermediate