A closure keeps variables alivestep through it
function outer() {
let name = 'Ana'; // in outer's scope
function inner() {
console.log(name); // captured ↑
}
return inner; // outer returns
}
const greet = outer(); // outer is DONE
greet(); // still prints 'Ana'
// console output appears here…
Scope & the Backpack
global scope
greet = ?
function outer()
name = 'Ana'
function inner() 🎒
reads
name from outer scope
Press Step to watch what happens. The key moment: after
outer() returns, its name variable would normally be destroyed — but inner captured it in its backpack, so it stays alive.
The Classic Interview Bugrun all 3
// Each creates 3 setTimeout callbacks
for (var i = 0; i < 3; i++) {
setTimeout(() =>
console.log(i)
, 100);
}
What you'll see:
var prints 3, 3, 3 — all callbacks share one i. let and the IIFE print 0, 1, 2 — each callback captures its own copy.
Captured Values (after timers fire)
var
?
?
?
let
?
?
?
IIFE
?
?
?
// var: one shared binding (function-scoped)
// → all callbacks see final i = 3
// let: new binding per iteration (block-scoped)
// → each callback captures its own i
// IIFE: passes i as an argument each loop
// → each call gets its own 'captured' param
Counter — Private State
Live Output
// run the pattern to see output…