Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 214
Advanced

JavaScript Closures

Understand how closures allow inner functions to retain access to variables from parent scopes with examples.

TL;DR

  1. 01Ensure inner functions retain access to their defining parent scopes.
  2. 02Store persistent private data state safely without using global variables.
  3. 03Resolve outer variables based on where functions are statically defined.

Tips

  1. 01Expose public API methods while keeping raw state hidden inside an enclosing closure scope function.
  2. 02Choose closures over standard class definitions when you only need to store small private states.

Warnings

  1. 01Avoid creating unnecessary closures enclosing huge objects because they can generate substantial memory leaks.
  2. 02Declare loop indexes using let so that each iteration receives its own distinct variable binding.
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 215
Advanced

JavaScript Closures

(continued)

What Closures Are

  • Closure definition

    Keeps reference access to outer scope variables even after parent execution finishes.

    function outer() {
      let n = 0;
      return () => ++n;
    }
    const count = outer();
    count(); // 1
  • Scope nesting

    Forms closures automatically whenever you nest child functions inside parent contexts.

    function parent() {
      const x = 1;
      function child() { return x; }
    }
  • Memory persistence

    Retains outer scope values in memory as long as the child function exists.

    const fn = outer(); // n stays in memory
  • Lexical scope

    Resolves variable scopes statically based on where the functions are declared.

    const x = 10;
    function test() { console.log(x); }
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 216
Advanced

JavaScript Closures

(continued)

Closures and Loops

  • var in loop

    Shares a single variable reference across all loop callbacks, causing bugs.

    for (var i = 0; i < 3; i++) {
      setTimeout(() => console.log(i));
    }
    // logs 3, 3, 3
  • let in loop

    Creates a new variable binding block per loop iteration to fix sharing.

    for (let i = 0; i < 3; i++) {
      setTimeout(() => console.log(i));
    }
    // logs 0, 1, 2
  • IIFE capture

    Caps variable values per iteration loop by wrapping functions in IIFE scopes.

    for (var i = 0; i < 3; i++) {
      (v => setTimeout(() => console.log(v)))(i);
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 217
Advanced

JavaScript Closures

(continued)

Private Variables

  • State encapsulation

    Stores internal variable values safely away from the global execution context.

    function createCounter() {
      let count = 0;
      return {
        increment: () => ++count,
        get: () => count
      };
    }
  • Public API access

    Exposes interface methods to read and write private variables under control.

    const c = createCounter();
    c.increment();
    console.log(c.get()); // 1
  • Accidental mutation

    Prevents external script scripts from corrupting or writing internal state values directly.

    let c = createCounter();
    // c.count is undefined
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 218
Advanced

JavaScript Closures

(continued)

Function Factories

  • Behavior configuration

    Creates functions sharing standard behaviors but retaining distinct internal configurations.

    function makeAdder(x) {
      return y => x + y;
    }
    const add5 = makeAdder(5);
    add5(10); // 15
  • Private memoization

    Closes over a private Map cache to return cached function outputs.

    function memoize(fn) {
      const cache = new Map();
      return x => {
        if (cache.has(x)) return cache.get(x);
        const res = fn(x);
        cache.set(x, res);
        return res;
      };
    }
Notes
Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 219
Advanced

JavaScript Closures

(FAQ)

FAQ

Closures inside a var loop share one variable reference. The loop terminates before the callbacks execute. Change the declaration to let to bind a fresh variable index per iteration.

Declare local variables inside a parent function and return helper functions accessing them. The returned helpers close over the state. External operations cannot inspect or alter this private state directly.

A function becomes a closure when it references variables outside its scope after the parent context exits. The closure keeps these external variables alive in memory. Normal functions only use parameters.

A factory function accepts configuration values and returns specialized functions enclosing those values. For example, makeAdder(5) returns a helper that always adds five. This keeps logic parameterized and clean.

Avoid capturing large object references that you do not need. Destructure only specific values required by the inner function. Nullify large variable handles once they are no longer needed.

Useful Cheatsheetsusefulcheatsheets.com
JavaScript Closures
Chapter 27 · Page 220
Advanced

JavaScript Closures

(In Practice)
In Practice

Debouncing Input with Closures

A debounce factory function closes over a timer reference to ensure that rapid handlers only execute once typing pauses.

  1. 01Declare a local variable to hold the active timeout identifier.
  2. 02Return a closure function that accepts arguments and intercepts calls.
  3. 03Clear any existing scheduled timeout to cancel the previous call.
  4. 04Schedule a new timeout to execute the target function after a delay.
  5. 05Forward the function arguments to the target handler on execution.
function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn(...args);
    }, delay);
  };
}
Takeaway

The closure over the timer variable keeps it alive between calls without polluting the global variable namespace.