← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

JavaScript Closures

javascript ES5 Intermediate
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). ESLint can catch the classic `var` in loop pattern with certain rules (e.g. no-loop-func), but the broader closure misuse — capturing references instead of values, memory leaks from large objects held in closure scope — requires careful code review or runtime/heap profiling to surface. The detection_hints list ESLint and TypeScript as tools, but they only catch the narrowest pattern, leaving many closure bugs invisible until runtime.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix cites replacing `var` with `let`/`const` inside loops, which is a direct pattern replacement within a localized area of code. Memory leak issues may require a bit more thought (nulling references, restructuring callbacks), but the fix is generally contained to one component rather than cross-cutting.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). Closures are a pervasive JavaScript concept but the debt from misuse is typically localized — loop closure bugs affect the specific loop or callback, and memory issues affect the specific module or component. The applies_to covers web and cli broadly, but the structural weight falls on individual features or components rather than shaping the whole codebase.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field directly states developers believe closures are a special function type, when in fact every function is a closure. Combined with the common_mistakes — especially capturing variable references not values, and the classic var-in-loop bug — this contradicts intuition from other languages where loop variables are naturally scoped per iteration. This is a well-documented gotcha that trips up developers moving from other languages and even experienced JS developers.

About DEBT scoring →

Also Known As

JavaScript closures closure scope lexical closure

TL;DR

A function that retains access to its enclosing scope's variables even after the outer function has returned — the foundation of data privacy in JS.

Explanation

A closure is created every time a function is defined — it captures the surrounding lexical environment (variables in scope at definition time). This enables: private state (a counter variable accessible only through returned increment/decrement functions), partial application, module patterns, and event handler factories. The classic loop bug: for(var i=0;i<3;i++) setTimeout(()=>console.log(i),0) logs 3 three times because all closures share the same var i. Fix: use let (block-scoped) or an IIFE wrapper. PHP closures (anonymous functions) work similarly but require explicit use($var) to capture outer variables — they don't capture automatically.

Diagram

flowchart TD
    OUTER[Outer function<br/>let count = 0] --> INNER[Inner function<br/>returns count++]
    INNER --> CLOSURE[Closure<br/>captures count variable<br/>not its value]
    CLOSURE --> EX1[call counter - returns 1]
    EX1 --> EX2[call counter - returns 2]
    EX2 --> EX3[call counter - returns 3]
    subgraph Common Use Cases
        PRIV[Private state - module pattern]
        CB[Callbacks with context]
        PARTIAL[Partial application]
        MEMO[Memoisation cache]
    end
style OUTER fill:#6e40c9,color:#fff
style CLOSURE fill:#238636,color:#fff
style EX3 fill:#1f6feb,color:#fff

Common Misconception

Closures are a special type of function in JavaScript. Every function in JavaScript is a closure — it captures its lexical scope at definition time. The term is typically used when a function accesses variables from an outer scope after that outer function has returned.

Why It Matters

Closures give inner functions access to their outer scope even after the outer function has returned — they are the foundation of JavaScript's module pattern, event handlers, and factory functions.

Common Mistakes

  • The classic loop closure bug — var in a for loop shares the same variable across all closures.
  • Creating closures in tight loops that hold large objects — memory stays allocated as long as closures exist.
  • Not understanding that closures capture the variable reference, not its value at creation time.
  • Using closures for private state when class private fields (#field) are more explicit.

Code Examples

✗ Vulnerable
// Classic loop closure bug with var:
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // Logs 3, 3, 3 — all share same 'i'
}

// Fixed with let (block scope) or IIFE:
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100); // Logs 0, 1, 2 — each iteration has own 'i'
}
✓ Fixed
function makeCounter() {
  let count = 0;           // private — not accessible outside
  return {
    inc: () => ++count,
    get: () => count,
  };
}
const c = makeCounter();
c.inc(); c.inc();
console.log(c.get()); // 2

Added 15 Mar 2026
Edited 22 Mar 2026
Views 32
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 3 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 1 ping M 0 pings T 0 pings W 1 ping T 2 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S
No pings yesterday
Amazonbot 9 Google 6 SEMrush 3 Unknown AI 2 Ahrefs 2 Majestic 1 ChatGPT 1 Perplexity 1 Qwen 1
crawler 24 crawler_json 1 pre-tracking 1
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Medium
⚡ Quick Fix
Use let/const inside loops instead of var to create proper per-iteration closures; understand that closures capture the reference not the value
📦 Applies To
javascript ES5 web cli
🔗 Prerequisites
🔍 Detection Hints
var in loops with async callbacks where loop variable captured by closure; IIFE pattern for module isolation
Auto-detectable: ✓ Yes eslint typescript
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: Function Tests: Update

✓ schema.org compliant