Closures and Scope
You've written a function that returns another function, or passed a callback into a loop, and watched it behave in a way that felt almost spiteful. Every button logs the same number. Every handler sees the last item, never the one it was supposed to. The code reads correctly top to bottom, and yet it lies. That moment - when a function clearly remembers the wrong thing - is the door into one of the deepest, most useful ideas in programming.
The idea is small once you see it: a function carries a backpack of the variables it was born next to, and it keeps reaching into that backpack long after the surrounding code has finished. That backpack is a closure, and the rules for what goes into it are called scope. Get these two right and the spiteful bugs stop being mysteries. They become predictable, even obvious - and the same trick that caused the bug becomes the tool you reach for to build private state and clean callbacks.
This guide uses JavaScript and Python for examples because that's where most people first hit this, but the idea is universal: every language with first-class functions - Swift, Go, Rust, C#, Kotlin, Ruby - works the same way underneath.
How to read this
- Want the one-sentence version? A closure is a function plus the variables it captured from the scope where it was defined (not where it's called). Read Phase 1 and you'll have the spine of it.
- Want it to finally click? Read in order. We build the model (scope), then use it the way you actually will (private state, callbacks), then walk straight into the famous loop bug and its fix.
The phases
- Scope and the Backpack - what lexical scope means, why a function can see the variables around where it was written, and the exact definition of a closure: the function plus its captured environment. The mental model everything else rests on.
- Closures You'll Actually Write - the everyday uses: a counter that keeps private state nothing else can touch, a function that pre-loads an argument, and callbacks that remember their context. Closures stop being a curiosity and become a tool.
- The Loop Bug and Other Gotchas - the classic trap where every callback sees the last value of a loop variable, why it happens (capture is by reference, not by snapshot), and the small fixes that solve it in JavaScript and Python - plus the memory gotcha closures can hide.
This guide is about the model - why functions remember and how to reason about it. We stay on the concepts that transfer to every language rather than cataloguing one runtime's edge cases. For the layer underneath - how variables and stack frames actually live in memory - see What Happens When Code Runs.