var Loop Closure Behavior

When callbacks close over a `var` loop variable, they share the same function-scoped binding. If the callbacks run after the loop finishes, they commonly all observe the variable's final value.

In JavaScript, `var` is function-scoped rather than block-scoped. A `var` declared in a loop therefore does not create a fresh binding for each iteration of the loop.

A closure retains access to the variable binding, not a snapshot of its current value. Multiple callbacks created during the loop can consequently reference the same `var` binding.

If those callbacks execute later, such as through timers or event handlers, the loop may already have completed. At that point the shared variable contains its final assigned value, so every callback can read that same value.

The usual modern fix is `let`, whose loop semantics create a distinct binding for each iteration. Another approach is to explicitly create a new scope per iteration, for example with an immediately invoked function expression (IIFE).

Key points

Interview questions

Why do callbacks inside a `var` loop often all print the same value?

`var` creates a function-scoped variable rather than a new binding for each loop iteration. The callbacks close over that one shared binding. If they execute after the loop finishes, the binding contains the loop's final value. Therefore, each callback reads the same value.

What is the simplest modern fix for the `var` loop closure issue?

Use `let` for the loop variable. A `let` declaration in a loop provides a separate per-iteration binding for closures to reference. As a result, each callback observes the value associated with its iteration. This is generally clearer than manually creating a scope.

Does a closure capture a value or a variable?

A closure retains access to the lexical environment containing the referenced variable. It does not generally create an independent snapshot of that variable's value. If the variable is later changed, the closure can observe the changed value. This distinction explains the classic `var` loop behavior.

How could you fix the problem without changing `var` to `let`?

One option is to create a new function scope for every iteration, commonly with an IIFE. The current loop value can be passed as an argument, and the callback closes over that function's parameter. Each invocation then has its own parameter binding. This was a common pre-ES6 technique.