Interactive exploration of lexical scope, closure-captured state, loop-variable bugs, and this binding rules.
Each counter is created by makeCounter() and closes over its own private count.
Increment one — the others stay unchanged. That is independent lexical environments, not shared globals.
function makeCounter(label) { let count = 0; // private — captured by returned closure return { inc() { count++; return count; }, get() { return count; }, label, }; }
var vs let
Three setTimeout callbacks scheduled in a loop. With var, all closures share one binding (logs 3,3,3).
With let, each iteration gets a fresh binding (logs 0,1,2).
this binding explorer
The same function body resolves this differently depending on call site, not declaration site.
Click each scenario to invoke the function and see what this becomes.