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…
Read the tutorial