What are JavaScript closures with practical examples?

A closure keeps access to variables from its outer scope after that scope has returned. In practice it powers several patterns:

  • Private state — a counter that only its returned functions can change.

  • Function factories — generate preset functions, like a multiplier.

  • Memoization — keep a private cache between calls.

  • Event handlers — remember data for later, asynchronous use.

Classic bug: a var loop with setTimeout shares one variable — switch to let (or an IIFE) to capture each value.

// 1. Counter with private state
function createCounter(initial = 0) {
  let count = initial; // Private
  return {
    increment: () => ++count,
    decrement: () => --count,
    reset: () => { count = initial; },
    value: () => count
  };
}
const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
// count is inaccessible directly

// 2. Once function (runs only once)
function once(fn) {
  let called = false;
  let result;
  return function(...args) {
    if (!called) {
      called = true;
      result = fn.apply(this, args);
    }
    return result;
  };
}

const initDB = once(() => {
  console.log('Connecting to DB...');
  return { connected: true };
});
initDB(); // 'Connecting to DB...' -> { connected: true }
initDB(); // Returns cached { connected: true } (no log)

// 3. Memoize
function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const expensiveCalc = memoize((n) => {
  console.log('Computing...');
  return n * n;
});
expensiveCalc(5); // 'Computing...' -> 25
expensiveCalc(5); // 25 (cached, no log)

Counter: count is private, accessible only through returned methods (closure). Once: called flag and result are in the closure — the function runs only on the first call, subsequent calls return the cached result.

Memoize: cache Map is in the closure — stores results by argument hash, returning cached values on repeat calls.

Show three practical closure patterns: counter (privacy), once (single execution), and memoize (caching). These demonstrate closures solving real problems, not just theory.

The once function is used for database connections, API initialization, and event handling.

What are JavaScript closures with practical examples? | Hiprup