← Concepts

阅读中文版本

Closures and Lexical Scope

A closure is a function that captures variables from its surrounding lexical scope, preserving access to those variables after the outer function has returned.

language-fundamentals

Definition

A closure is created when a function references variables from an outer enclosing function's scope. The JavaScript engine preserves the outer function's variable environment in memory as long as the closure exists, even after the outer function has finished executing. Lexical scope means that variable visibility is determined by the physical nesting of functions in source code, not by the call stack at runtime. A closure 'closes over' (captures) the variables it references from outer scopes, maintaining their binding for the lifetime of the closure.

Context and scope

Closures are fundamental to JavaScript's module pattern, event handlers, callbacks, memoization, and functional programming utilities. They explain why an inner function can access outer variables long after the outer scope would normally have been garbage-collected. Every function in JavaScript forms a closure over the global scope; nested functions form closures over their containing function's scope.

Common misconceptions

  • Closures copy variable values at creation time — they capture live references, so changes to the captured variable are visible inside the closure
  • Only arrow functions create closures — all JavaScript functions create closures over their lexical environment
  • Closures cause memory leaks by default — they only prevent GC of captured variables; if the closure itself is no longer reachable, collected variables are freed

Anti-patterns

  • Creating functions inside a loop that reference the loop variable without capturing its current value (classic var-in-loop problem)
  • Holding closures over large DOM trees or data arrays that are no longer needed
  • Using closures to simulate private state when modern JavaScript provides private class fields (# prefix)

Related concepts

Authoritative source

https://tc39.es/ecma262/#sec-closure

What Are Closures and Lexical Scope

A closure gives a function persistent access to variables from an outer scope. Lexical scope determines which variables are visible where based on source code structure, not runtime call order.

How Lexical Scope Works

JavaScript resolves variable references by walking up the scope chain at the location where the function is defined — not where it’s called:

const greeting = 'global';

function outer() {
  const greeting = 'outer';
  
  function inner() {
    console.log(greeting); // "outer" — resolved at definition site
  }
  
  return inner;
}

const fn = outer();
fn(); // "outer" (not "global")

Closures in Practice

Module Pattern

const counter = (function() {
  let privateCount = 0; // captured by closure
  
  return {
    increment: () => ++privateCount,
    getCount: () => privateCount,
  };
})();

counter.increment();
console.log(counter.getCount()); // 1
// privateCount is not accessible directly

Function Factory

function createMultiplier(multiplier) {
  return function(x) {
    return x * multiplier; // captures 'multiplier'
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15

Common Pitfall: Loop Variable Capture

// ❌ Bug: all callbacks log '5'
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100);
}

// ✅ Fix: use let (block-scoped per iteration)
for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 100); // 0,1,2,3,4
}

When to Use Closures

  • Encapsulating private state (module pattern)
  • Creating specialized functions from general ones (currying, partial application)
  • Preserving configuration across async callbacks
  • Implementing memoization caches

Memory Considerations

Closures keep captured variables in memory. If a closure is long-lived, avoid capturing large objects that aren’t needed — set them to null when done.

  • Lexical Scope: Static scope resolution
  • Garbage Collection: When captured variables become eligible for collection
  • Private Class Fields: Modern alternative for encapsulation