🔗

Closures & Scope Chain — How Functions Remember Outer Variables

The internal structure of scope chains created by Lexical Environment objects

function makeCounter() {
  let count = 0;
  return function() { return ++count; };
}
const counter = makeCounter();
counter(); // 1
counter(); // 2 — count is still alive

makeCounter() already returned, but the inner function still uses count. This is a closure.

V8 Internals — Lexical Environment

Every function execution creates a Lexical Environment object storing local variables and an outer reference.

The anonymous function's [[Environment]] internal slot references makeCounter's Lexical Environment. As long as this reference exists, count won't be GC'd.

On counter() call: look for count in own Lexical Environment → not found → follow outer → find count in makeCounter's environment. This is the scope chain.

Memory Leak Warning

Closures referencing outer variables prevent that environment from being GC'd. Event listeners with closures that aren't removed cause memory leaks.

Key Points

1

On function creation, current Lexical Environment saved in [[Environment]] slot

2

Variable lookup follows scope chain: own env → outer → outer...

3

If a function referencing outer variables is alive, that Lexical Environment won't be GC'd

4

Closures in event listeners must be released with removeEventListener

Use Cases

Data hiding — implement private variables with closures (module pattern) Currying/partial application — create functions with pre-fixed arguments

References