Output Questions: Closures & Loops

Predict the output — closure capture, stale closures, IIFE patterns, loop bugs, and memoization gotchas.

must medium ⏱ 25 min closuresloopsiifestale-closurecapture
Mastery:
Why interviewers ask this
Closure output questions are the most common JS interview questions. They test whether you understand that closures capture references, not values.

Q1 — basic closure capture

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
1
2
3
1

inner closes over count by reference, not by value. Each call to fn() reads and increments the same count variable in outer’s scope. fn2 is a fresh invocation of outer, creating an independent count starting at 0.


Q2 — classic var loop bug

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
3
3
3

All three functions close over the same i variable (because var is function-scoped, not block-scoped). By the time any function is called, the loop has finished and i === 3.


Q3 — IIFE fix for the loop bug

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
0
1
2

The IIFE immediately invokes a new function with i passed as the argument j. Each iteration creates a new scope with its own j, freezing the current value of i. The inner function closes over j, which is independent per iteration.


Q4 — closure over a variable that changes

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
10
20

Closures capture variables by reference, not snapshot. getX always reads the current value of x. When x changes to 20, the next call sees 20. This is why stale closures in React happen — a hook’s closure captures the value at the time the function was created, but if the variable changes and the function isn’t recreated, it sees the old value.


Q5 — multiple closures sharing a variable

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
2

All three methods close over the same count — they share the binding. increment is called 3× (count = 3), decrement once (count = 2).


Q6 — function factory with parameter

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
10
15
12

double closes over factor = 2; triple over factor = 3. They’re independent closures with different factor values. triple(2) = 6, then double(6) = 12.


Q7 — stale closure in setTimeout

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
42

The callback closes over val by reference. By the time the setTimeout fires (100ms later), val has been updated to 42. It reads the current value at execution time, not a snapshot from when setTimeout was called.


Q8 — closure inside object method

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
undefined

The inner function is a plain function, not a method. When called as fn() (not obj.something()), this is the global object (window in browser) or undefined in strict mode. window.value is undefined. Fix with () => (arrow inherits outer this) or .bind(obj).


Q9 — IIFE output

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
7
42
"undefined"

IIFEs execute immediately and return their value. result is 7. The second IIFE logs 42 internally. secret is scoped inside the IIFE — it doesn’t pollute the outer scope, so typeof secret is "undefined" (no ReferenceError because typeof on an undeclared name is safe).


Q10 — closure counter with reset

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
6
7
7
5

start and count are both in the closure. reset() sets count back to start (the original argument), which is still accessible because it’s part of the outer function scope.


Q11 — closure over let in switch

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
Error: Identifier 'msg' has already been declared

All case clauses in a switch share one block scope. The two let msg declarations conflict — you get a SyntaxError at parse time. Fix: wrap each case body in {} to give each case its own block.

case 1: { let msg = 'one'; ... break; }
case 2: { let msg = 'two'; ... break; }

Q12 — closure in class method

Run it yourself
Edit and run. Output is captured from console.log. Async logs (setTimeout/Promise) appear in real execution order.
Console
 
Answer & why
NaN (after each tick)

The function() callback loses this — inside a regular function passed to setInterval, this is window/undefined. window.seconds is undefined, and undefined++ is NaN. Fix: use an arrow function () => { this.seconds++; } — arrows inherit this from the enclosing class method.

The core rule
Closures capture variables by reference, not by value snapshot. That’s what powers counters and iterators — and what causes the var loop bug and stale closures in React hooks. When you need to freeze a value, either use let (fresh binding per iteration), an IIFE (new scope with value passed as argument), or copy the value into a new variable.

Likely follow-up questions
  • What does a closure capture — a value or a reference?
  • How does IIFE fix the var loop problem?
  • What is a stale closure and how do you avoid it in React?
  • Can closures cause memory leaks?

References