# Closures and Scope

> Why a function remembers variables from where it was born: scope, closures, and the captured-variable bugs that bite in every language with first-class functions.


---

# 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 instead of its own. 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](01-scope-and-the-backpack.md) 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
1. **[Scope and the Backpack](01-scope-and-the-backpack.md)** - 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.
2. **[Closures You'll Actually Write](02-closures-you-will-write.md)** - 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.
3. **[The Loop Bug and Other Gotchas](03-the-loop-bug-and-gotchas.md)** - 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](/guides/what-happens-when-code-runs).


---

# Scope and the Backpack

Before closures make sense, one smaller idea has to be solid: **scope** - the set of variables a piece of code is allowed to see. You already use scope every day without naming it: inside a function you can read variables declared outside it, but outside that function you can't reach variables declared inside. That's scope drawing its lines.

The lines are not random. They follow a rule so reliable you can read them straight off the page, and that rule is the whole foundation.

## Lexical scope: where it's written, not where it's called

Most languages you'll meet use **lexical scope** (also called static scope). "Lexical" means *based on the text* - where a thing is physically written in the source code. A function can see the variables of the region of code it was *written inside*, regardless of where it later gets called from.

Picture nested boxes. Each function draws a box around its code. Inner boxes can see out into the boxes that enclose them; outer boxes cannot see in.

```text
outer box (file / module)
│  name = "Ada"
│
│  ┌─ greet() box ──────────────┐
│  │  can see: name  ✓          │
│  │                            │
│  │  ┌─ shout() box ────────┐  │
│  │  │  can see: name ✓     │  │
│  │  │  can see: greet vars │  │
│  │  └──────────────────────┘  │
│  └────────────────────────────┘
```

*What just happened:* Looking up a variable walks *outward* through the boxes that physically enclose the code - `shout` checks itself, then `greet`, then the file - and stops at the first match. This outward walk is called the **scope chain**, and because it follows the text, you can trace it by eye without running anything.

Here it is in real code:

```python
name = "Ada"

def greet():
    greeting = "Hello"
    print(greeting, name)   # sees greeting (own box) and name (outer box)

greet()
```

*What just happened:* `greet` reads `greeting` from its own scope and `name` from the enclosing module scope. The lookup walked outward and found each name in the nearest box that had it.

The opposite would be **dynamic scope**, where a function sees variables from wherever it was *called*. Almost no mainstream language works that way today, because it makes code impossible to reason about - you'd have to know every call site to know what a function can see. Lexical scope is the sane default, and it's what makes the next idea possible.

## The move that creates a closure

Now do one thing that feels innocent: have a function *return another function*, and let that inner function use a variable from the outer one.

```python
def make_greeter(name):
    def greet():
        print("Hello,", name)   # name comes from make_greeter's scope
    return greet

hi = make_greeter("Ada")
hi()                            # Hello, Ada
```

*What just happened:* `make_greeter` ran, created `greet`, and returned it - then `make_greeter` itself finished and its local `name` would normally vanish. But `hi()` still printed `Ada`. The inner function kept the variable alive.

That is the entire phenomenon. When `greet` was *defined*, it captured a reference to `name` from the box it was born in. When we returned `greet` and `make_greeter` exited, `greet` carried `name` out with it. The function plus the variables it captured is the closure.

💡 **The one-line definition.** A **closure** is a function bundled together with the variables it captured from the scope where it was *defined*. The function is the code; the closure is the code *plus its remembered environment*.

## The backpack

Here's the picture that makes it stick. When a function is created, it packs a backpack with references to the outer variables it uses. Wherever the function travels - returned, stored in a list, passed across the program - the backpack goes with it. Call the function much later, in a completely different part of the code, and it reaches into the backpack and finds those variables exactly as they were left.

```javascript
function makeCounter() {
  let count = 0;
  return function () {     // this function packs `count` in its backpack
    count += 1;
    return count;
  };
}

const next = makeCounter();
console.log(next());   // 1
console.log(next());   // 2
console.log(next());   // 3
```

*What just happened:* `makeCounter` finished after the first line of use, yet `count` survived and kept incrementing. `next` is holding a backpack containing `count`, and each call reaches in, bumps it, and puts it back. The variable lives as long as the closure that holds it does.

One detail that matters for everything ahead: the backpack holds a *reference to the variable*, not a frozen copy of its value. That's why `count` can change between calls. It's the same `count`, not a snapshot taken at creation time. Hold onto that - it's the seed of the famous bug in Phase 3.

## Why this exists at all

Closures aren't an accident of syntax; they're what makes functions genuinely *first-class*. If you can pass a function around like any other value, that function needs to keep working no matter where it lands - and "keep working" means still having access to the data it depended on. Without closures, a returned or stored function would be a half-broken thing, referring to variables that no longer exist - closures are the mechanism that lets a function be a self-contained, portable unit of behavior *plus* the state it needs.

> **In the wild.** Every event handler, every callback you pass to `map` or `setTimeout`, every decorator, every "remember this config and use it later" helper - all of them lean on closures. You've been using them since your first callback, whether or not anyone named it.

```quiz
[
  {
    "q": "Under lexical scope, what determines which variables a function can see?",
    "choices": ["Where the function is called from", "Where the function is written in the source", "The order functions were defined", "Which thread runs the function"],
    "answer": 1,
    "explain": "Lexical (static) scope is based on the text - a function sees the variables of the region it was physically written inside, not wherever it's later called."
  },
  {
    "q": "What exactly is a closure?",
    "choices": ["Any function that takes another function as an argument", "A function plus the variables it captured from its defining scope", "A function with no parameters", "A copy of a function stored in a variable"],
    "answer": 1,
    "explain": "A closure is the function bundled with the variables it captured from the scope where it was defined - the code plus its remembered environment."
  },
  {
    "q": "In makeCounter, why does count keep increasing across separate calls to the returned function?",
    "choices": ["count is re-created as 0 on every call", "The closure captured a reference to the same count variable, which persists", "count is a global variable", "JavaScript caches return values"],
    "answer": 1,
    "explain": "The backpack holds a reference to count, not a fresh copy. The same variable survives between calls and keeps its value."
  }
]
```


---

# Closures You'll Actually Write

Phase 1 gave you the model: a function carries a backpack of captured variables. That sounds abstract until you see what it *buys* you. Three patterns cover most of what closures are actually for, and once you recognize them you'll spot them everywhere in real code - and start reaching for them on purpose.

## Pattern 1: private state

A variable inside a function is invisible to the outside world. Combine that with a closure and you get state that *persists* between calls but that nothing else can reach, read, or corrupt. No class, no `private` keyword - the scope itself is the wall.

```javascript
function makeAccount(start) {
  let balance = start;                  // private - only the closures below can touch it

  return {
    deposit(amount) { balance += amount; return balance; },
    withdraw(amount) {
      if (amount > balance) return "insufficient funds";
      balance -= amount;
      return balance;
    },
    check() { return balance; }
  };
}

const acct = makeAccount(100);
console.log(acct.deposit(50));   // 150
console.log(acct.withdraw(30));  // 120
console.log(acct.balance);       // undefined - there is no such property
```

*What just happened:* `balance` lives in `makeAccount`'s scope. The three returned functions all share that one `balance` through their backpacks, so they stay coordinated. But there's no way to reach `balance` from outside - `acct.balance` is `undefined` because `balance` was never a property, only a captured variable. The only way to change it is through `deposit` and `withdraw`, exactly as intended.

This is real encapsulation, built from nothing but scope. The data is protected not by a rule the language enforces with a keyword, but by the simple fact that no code outside the closure has any name for the variable.

💡 **Why this matters.** "Private state" usually makes people think of classes. Closures got there first and got there simpler: any time you want some data that a few functions share and outsiders can't reach, a closure does it without ceremony.

## Pattern 2: pre-loading an argument

Sometimes you have a general function and you want a specialized version with one argument already filled in. A closure captures that argument and hands back a smaller, ready-to-use function.

```python
def multiplier(factor):
    def multiply(n):
        return n * factor      # factor is captured from the enclosing call
    return multiply

double = multiplier(2)
triple = multiplier(3)

print(double(10))   # 20
print(triple(10))   # 30
```

*What just happened:* Calling `multiplier(2)` baked `factor = 2` into the `double` closure's backpack; `multiplier(3)` baked `3` into `triple`. Each returned function remembers its own `factor`, so `double` and `triple` are genuinely different functions made from the same template. This pattern - fixing one argument to produce a more specific function - is called **partial application**, and closures are how it's built.

You'll see this constantly: a logger pre-loaded with a category, a fetch helper pre-loaded with a base URL, a validator pre-loaded with a set of rules. One general function becomes a family of tailored ones, each carrying its own configuration.

## Pattern 3: callbacks that remember

This is where closures earn their keep most often. A callback is a function you hand to someone else to call *later* - when a button is clicked, when data arrives, when a timer fires. By the time it runs, the code that created it has long since moved on, so the callback needs to remember the context it was born in - a closure is precisely that memory.

```javascript
function setupButtons(labels) {
  for (const label of labels) {
    const button = createButton(label);
    button.onClick(function () {
      console.log("You clicked:", label);   // each callback remembers its own label
    });
  }
}

setupButtons(["Save", "Cancel", "Delete"]);
```

*What just happened:* Each click handler is created inside the loop body and captures the `label` that existed at that moment. When a click happens much later, the handler reaches into its backpack and finds the right label - "Save", "Cancel", or "Delete" - even though `setupButtons` finished running the instant it was called. The callback carried its context with it.

Notice this works cleanly here because `label` is declared with `const` *inside the loop body*, so each iteration creates a fresh `label`. That detail is doing quiet, critical work - and in Phase 3 you'll see exactly what goes wrong when that detail is missing.

> **For builders.** Callbacks are everywhere async code lives. When you pass a function to `setTimeout`, an event listener, or a promise's `.then`, you're handing off a closure that has to remember what it was working on. The same backpack that makes a counter work makes async callbacks coherent. For how those callbacks get scheduled and run later, see [Async/Await & the Event Loop](/guides/async-await-and-the-event-loop).

## The common thread

All three patterns are the same physics from Phase 1, pointed at different jobs:

- **Private state** - captured variables outlive the function, and outsiders have no name for them.
- **Pre-loading** - a captured argument specializes a general function.
- **Callbacks** - a captured context travels with a function to wherever it's called later.

You don't need three mental models. You need one - *a function carries the variables it was born next to* - and the uses fall out of it.

```quiz
[
  {
    "q": "In makeAccount, why can outside code not directly read or change balance?",
    "choices": ["balance is marked private", "balance is a captured variable with no name outside the closure", "JavaScript freezes returned objects", "balance is stored on the prototype"],
    "answer": 1,
    "explain": "balance is a local variable captured by the closures. Outside code has no name for it, so the only access is through the returned functions."
  },
  {
    "q": "After double = multiplier(2) and triple = multiplier(3), what makes double and triple behave differently?",
    "choices": ["They share one factor variable", "Each closure captured its own factor from a separate call", "Python re-evaluates multiplier on every call", "factor is a global that toggles"],
    "answer": 1,
    "explain": "Each call to multiplier created a new scope with its own factor, and the returned function captured that specific one. double remembers 2, triple remembers 3."
  },
  {
    "q": "Why is a closure the natural fit for a callback that runs later?",
    "choices": ["It runs faster than a named function", "It remembers the context it was created in, even after that code finished", "It prevents the callback from being called twice", "It copies all global variables"],
    "answer": 1,
    "explain": "By the time a callback fires, its creating code is long gone. The closure carries the captured context with it, so the callback still knows what it was working on."
  }
]
```

Watch it animated: [closures](/explainers/Closures.dc.html)


---

# The Loop Bug and Other Gotchas

Now the bug that sends people searching at midnight: the single most common closure mistake, appearing in nearly every language with first-class functions, looking so wrong that people assume the language is broken. It isn't - the behavior follows directly from the one rule you already know: closures capture the *variable*, not a snapshot of its value. Once you see that, the bug becomes inevitable instead of mysterious.

## The bug

You build a list of functions in a loop. Each one should remember the loop counter at its moment of creation. Watch what they actually remember.

```javascript
const funcs = [];
for (var i = 0; i < 3; i++) {
  funcs.push(function () {
    console.log(i);
  });
}

funcs[0]();   // 3
funcs[1]();   // 3
funcs[2]();   // 3
```

*What just happened:* You expected `0, 1, 2`. You got `3, 3, 3`. Every function printed the same value - and not even a value any function was "supposed" to see, but the value `i` ended on *after the loop finished*.

The same thing happens in Python:

```python
funcs = []
for i in range(3):
    funcs.append(lambda: print(i))

funcs[0]()   # 2
funcs[1]()   # 2
funcs[2]()   # 2
```

*What just happened:* Three lambdas, all printing `2` - the last value `i` held. Identical bug, identical cause.

## Why it happens

Here's the part that turns confusion into understanding. With `var` in JavaScript (and with any plain loop variable in Python), there is **one** variable `i`, shared by every iteration. The loop doesn't make a new `i` each time around - it reuses the same one, changing its value.

Each closure captured a reference to that *one shared variable*. They didn't each grab a copy of `i`'s value at the instant they were created; they all grabbed a reference to the same box labeled `i`. By the time you actually *call* the functions, the loop is long over and that one box holds its final value. Every closure looks in the same box and sees the same thing.

```text
ONE shared i  ──►  [ 3 ]
                    ▲ ▲ ▲
        func0 ──────┘ │ │   all three closures point
        func1 ────────┘ │   at the same box, read it
        func2 ──────────┘   at call time → all see 3
```

*What just happened:* The closures are working *correctly* - they each read the live value of the variable they captured. The mistake was assuming "capture" means "take a photo now." It means "keep a pointer to the box." This delayed read is sometimes called **late binding**: the value is looked up when the function runs, not when it's defined.

## The fix: give each iteration its own variable

The cure is to stop sharing one variable. Give every iteration a *fresh* variable, so each closure captures a different box.

In JavaScript, this is exactly what `let` does inside a loop - it creates a new binding per iteration:

```javascript
const funcs = [];
for (let i = 0; i < 3; i++) {   // let, not var
  funcs.push(function () {
    console.log(i);
  });
}

funcs[0]();   // 0
funcs[1]();   // 1
funcs[2]();   // 2
```

*What just happened:* With `let`, each pass through the loop gets its own `i`. There are now three separate boxes, and each closure captured a different one - so they print `0, 1, 2`. Changing one keyword fixed it, because the keyword changed how many variables exist.

Python has no per-iteration binding, so the idiomatic fix is to **capture the value explicitly** using a default argument, which is evaluated once, at definition time:

```python
funcs = []
for i in range(3):
    funcs.append(lambda i=i: print(i))   # i=i snapshots the current value

funcs[0]()   # 0
funcs[1]()   # 1
funcs[2]()   # 2
```

*What just happened:* `lambda i=i:` says "make `i` a parameter whose default is the *current* value of the loop's `i`." Default values are computed when the function is defined, so each lambda freezes its own number. You've forced a snapshot instead of relying on a shared reference.

💡 **The lesson in one line.** The bug isn't "closures are weird." It's "closures capture variables, and a plain loop reuses one variable." The fix is always the same idea: make sure each closure captures a *different* variable.

## The other gotcha: closures can hold memory hostage

There's a quieter trap worth knowing. A closure keeps its entire captured environment alive for as long as the closure itself exists. That's the whole point - but it means a long-lived closure can accidentally pin large objects in memory that you assumed were gone.

```javascript
function attach() {
  const hugeData = loadBigThing();        // large object
  document.addEventListener("scroll", function () {
    console.log("scrolled");              // never even uses hugeData
  });
}
```

*What just happened:* The scroll handler is a closure over `attach`'s scope, and as long as that listener stays registered, the whole scope - including `hugeData` - can't be garbage collected, even though the handler never touches it. The listener outlives the function, so its backpack does too. The fix is to scope large data tighter, or remove the listener when you're done with it.

> **In the wild.** This is a real source of memory leaks in long-running front ends and servers: event handlers and callbacks that quietly retain big objects through their captured scope. When a process slowly grows in memory and you can't find why, look for closures that outlive what they captured. For how reachable objects keep memory alive, the underlying mechanism is in [What Happens When Code Runs](/guides/what-happens-when-code-runs).

## Recap

1. **The loop bug** - closures created in a loop all see the loop variable's *final* value, because a plain loop reuses one shared variable and capture is by reference.
2. **Late binding** - a closure reads its captured variable when it *runs*, not when it's *defined*; the loop has finished changing the variable by then.
3. **The fix** - give each iteration its own variable: `let` in JavaScript, a default-argument snapshot (`i=i`) in Python.
4. **Memory** - closures keep their whole captured scope alive; a long-lived closure can pin large objects you thought were freed.

You now have the full model - scope, capture, the patterns, and the traps. The next time a function remembers the wrong thing, you won't guess. You'll know which box it's looking in.

```quiz
[
  {
    "q": "Why do all three functions print 3 in the var version of the loop?",
    "choices": ["The closures each copied i when created", "There is one shared i, and every closure reads its final value at call time", "var resets i to 3 automatically", "Functions in arrays share return values"],
    "answer": 1,
    "explain": "var creates a single i reused across iterations. All closures captured that one variable and read its final value (3) when called - late binding."
  },
  {
    "q": "How does using let instead of var fix the loop bug in JavaScript?",
    "choices": ["let makes the loop run faster", "let creates a new binding for each iteration, so each closure captures a different variable", "let copies the function body", "let disables closures inside loops"],
    "answer": 1,
    "explain": "let gives every iteration its own fresh i. With separate boxes, each closure captures a different one, so they print 0, 1, 2."
  },
  {
    "q": "Why might a long-lived event handler cause a memory leak?",
    "choices": ["Handlers are never garbage collected", "The closure keeps its entire captured scope alive, including large objects it doesn't use", "Each handler copies the whole heap", "Closures double their memory on every call"],
    "answer": 1,
    "explain": "A closure retains its captured environment as long as it exists. A persistent handler can pin large captured objects in memory even if it never references them."
  }
]
```
