# Closures and Scope

> Source: https://learn-javascript.org/closures-and-scope/
> Part of Learn JavaScript, free to read.

A closure is a function that remembers the variables where it was defined, even after that outer function has returned.

```javascript
function counter() {
  let count = 0;                 // lives on, because the inner function uses it
  return function () {
    count += 1;
    return count;
  };
}

const next = counter();
next();    // 1
next();    // 2

const other = counter();
other();   // 1 — its own independent count
```

`count` is not garbage collected when `counter()` returns, because the returned function still references it. Each call to `counter()` creates a fresh binding.

## Scope

```javascript
let outer = "visible everywhere below";

function example() {
  let fnScoped = "visible in this function";

  if (true) {
    let blockScoped = "only in this block";
    var functionScoped = "hoisted to the whole function";
  }

  console.log(blockScoped);      // ReferenceError
  console.log(functionScoped);   // works — this is why let is a problem
}
```

`let` and `const` are **block-scoped**; `var` is **function-scoped** and hoisted. That difference is the single best reason never to write `var`.

## The classic loop bug

The most cited closure gotcha, and a good demonstration of why:

```javascript
const fns = [];
for (var i = 0; i < 3; i++) {
  fns.push(() => console.log(i));
}
fns.forEach((f) => f());     // 3, 3, 3
```

There is only **one** `i` — `var` gives the whole function one binding — and by the time the callbacks run, the loop has finished and `i` is 3.

```javascript
for (let i = 0; i < 3; i++) {
  fns.push(() => console.log(i));
}
fns.forEach((f) => f());     // 0, 1, 2
```

`let` creates a **new binding per iteration**, so each closure captures its own. Using `let` in loops fixes this for free.

## Private state

Before `#private` class fields, closures were how JavaScript did encapsulation — and they are still a clean way to do it:

```javascript
function createAccount(openingCents) {
  let balanceCents = openingCents;          // genuinely inaccessible outside

  return {
    deposit(cents) {
      if (cents <= 0) throw new RangeError("must be positive");
      balanceCents += cents;
    },
    get balance() { return balanceCents / 100; },
  };
}

const acc = createAccount(1000);
acc.deposit(500);
acc.balance;          // 15
acc.balanceCents;     // undefined — there is no way to reach it
```

No `this`, no `new`, no binding problems. For many objects this is simpler than a class.

## Partial application

```javascript
function withPrefix(prefix) {
  return (message) => `[${prefix}] ${message}`;
}

const warn = withPrefix("WARN");
warn("disk almost full");      // "[WARN] disk almost full"
```

The returned function has `prefix` baked in. This is the pattern behind most middleware and configuration helpers.

## Closures and memory

The part that causes real bugs. **A closure keeps everything in its scope alive**, not just the variables it uses:

```javascript
function makeHandler(hugeBuffer) {
  const size = hugeBuffer.length;
  return () => console.log(size);      // still retains hugeBuffer in many engines
}
```

The safe pattern is to extract what you need and let the rest go:

```javascript
function makeHandler(hugeBuffer) {
  const size = hugeBuffer.length;
  return () => console.log(size);
}
// call it as: makeHandler(buf); buf = null;
```

The more common leak is a listener that is never removed:

```javascript
function subscribe(bigObject) {
  emitter.on("tick", () => use(bigObject));    // never removed
}
```

Every call adds a listener, each holding its captured scope, and the emitter's array grows forever. Node's `MaxListenersExceededWarning` is the tell — treat it as a leak report, not noise.

```javascript
function subscribe(bigObject) {
  const handler = () => use(bigObject);
  emitter.on("tick", handler);
  return () => emitter.off("tick", handler);   // give callers a way to unsubscribe
}
```

Returning an unsubscribe function is the pattern worth adopting everywhere — it is what React's `useEffect` cleanup is.

## Where you meet closures without noticing

```javascript
// every callback
items.map((item) => item.price * taxRate);      // captures taxRate

// event handlers
button.addEventListener("click", () => save(form));

// React hooks — a stale closure is the most common hook bug
useEffect(() => {
  const id = setInterval(() => setCount(count + 1), 1000);
  return () => clearInterval(id);
}, []);        // count is captured once and never updates — use the updater form
```

That last one is worth recognising: with an empty dependency array the effect captures `count` from the first render forever. `setCount((c) => c + 1)` avoids the capture entirely.

## Exercise

```javascript
// 1. Write once(fn) returning a function that runs fn only the first time
//    and returns the cached result after that.
// 2. Write createRateLimiter(maxCalls, windowMs) using closure state.
// 3. Explain in a comment why this logs 3,3,3 and fix it:
const fns = [];
for (var i = 0; i < 3; i++) fns.push(() => console.log(i));
fns.forEach((f) => f());
```

## Common questions

### Are closures slow?

No — they are fundamental to how JavaScript works and engines optimise them heavily. The cost is memory, not speed: a closure keeps its scope alive, so a long-lived one holding a large object keeps that object alive too.

### When should I use a closure instead of a class?

When you want genuine privacy with no `this`, or when an object has a small, fixed set of behaviours. Classes suit types with many methods, inheritance, or where a framework expects them. For a counter, a rate limiter or a configured helper, a closure is usually simpler.

### Why does my React state seem stale inside a callback?

Because the callback captured the value from the render it was created in. Either add the value to the dependency array so a fresh closure is made, or use the functional updater form, which reads the current value rather than a captured one.
