JavaScript Closures, Scope, and the Module Pattern: The Complete Guide

JavaScript Closures, Scope, and the Module Pattern: The Complete Guide

Closures are mentioned in every JavaScript interview but rarely understood deeply. A closure is a function that retains access to its outer scope even after the outer function has returned. This seemingly simple definition has profound implications for private state, module patterns, event handlers, and the classic loop variable bug that catches developers repeatedly.

TL;DR: Closure = function + reference to its outer lexical scope. Variables are captured by reference, not by value. The loop bug happens because var is function-scoped; fix with let or IIFE. Use closures for private state and factory functions. ES modules are the modern module pattern.

Lexical scope — how JavaScript looks up variables

// JavaScript uses lexical (static) scope — determined at write time, not run time
const x = 'global';
function outer() {
  const x = 'outer';
  function inner() {
    const x = 'inner';
    console.log(x); // 'inner' — finds x in own scope first
  }
  inner();
  console.log(x); // 'outer' — inner scope not accessible
}

// Scope chain: inner → outer → global → undefined
// Each function creates a new scope

Closures in depth — captured by reference

function makeAdder(x) {
  return function(y) { return x + y; }; // Closes over x
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
add5(3);  // 8 — x is 5 in add5's closure
add10(3); // 13 — x is 10 in add10's closure

// Captured BY REFERENCE — mutation is visible:
function makeCounter() {
  let count = 0;
  return {
    inc: () => ++count,
    dec: () => --count,
    val: () => count
  };
}
const c = makeCounter();
c.inc(); c.inc(); c.val(); // 2 — same count variable, three closures over it

The classic loop bug

// BUG: var is function-scoped, all closures share ONE i
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 3, 3, 3 (not 0, 1, 2)
}
// When setTimeout fires, loop is done, i = 3

// FIX 1: let (block-scoped, new binding per iteration)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

// FIX 2: IIFE (captures value at time of call)
for (var i = 0; i < 3; i++) {
  ((j) => setTimeout(() => console.log(j), 100))(i);
}

Module pattern — private state with closures

// IIFE module: creates private scope
const UserStore = (() => {
  let users = [];              // Private: inaccessible from outside
  let nextId = 1;

  return {                     // Public API
    add(name) {
      users.push({ id: nextId++, name });
    },
    getAll() {
      return [...users];       // Return copy, prevent external mutation
    },
    count() { return users.length; }
  };
})();

UserStore.add('Alice');
UserStore.getAll(); // [{ id: 1, name: 'Alice' }]
UserStore.users;    // undefined — private!

// ES Modules (modern approach — same concept, better syntax):
// users.js
let users = [];  // Module-scoped = effectively private
export function addUser(name) { users.push(name); }
export function getUsers() { return [...users]; }
  • ✅ Use let/const in loops — never var inside closures
  • ✅ Closures for private state — factory functions and module pattern
  • ✅ ES modules for modern code — native support, tree-shakeable
  • ✅ Return copies of arrays/objects from closures to prevent mutation
  • ❌ Never rely on closure variables being immutable — they are mutable references
  • ❌ Never create closures inside tight loops unnecessarily — memory overhead

Closures are central to the JavaScript interview guide — the loop bug appears in 80% of senior JS interviews. External reference: MDN Closures documentation.

Recommended Reading

Designing Data-Intensive Applications — The essential book every senior developer needs.

The Pragmatic Programmer — Timeless engineering wisdom for writing better code.

Affiliate links. We earn a small commission at no extra cost to you.

Free Weekly Newsletter

🚀 Don’t Miss the Next Cheat Code

Join 1,000+ senior developers getting expert JS, Python, AWS and system design secrets weekly.

✓ No spam✓ Unsubscribe anytime

Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply