JS CodeLab

JavaScript Closures Explained (with Real Examples)

By JS CodeLab Engineering Team•Last updated: October 12, 2026•5 min read

Direct Answer & Key Concept

A closure is the combination of a function bundled together with references to its surrounding lexical environment. Closures give an inner function access to an outer function's scope even after the outer function has finished executing.

Runnable Code SnippetRun in Playground
// Classic Counter Closure Example
function createCounter(start = 0) {
  let count = start;
  
  return {
    increment: () => ++count,
    decrement: () => --count,
    get: () => count
  };
}

const counter = createCounter(10);
console.log(counter.increment()); // 11
console.log(counter.increment()); // 12
console.log(counter.decrement()); // 11
console.log("Current:", counter.get()); // Current: 11

What is a closure in JavaScript?

A closure is a core JavaScript mechanism wherein an inner function retains access to variables and parameters declared in its outer (enclosing) function's lexical scope, even after the outer function has completed execution and returned.

In technical terms, in JavaScript, closures are created every time a function is created, at function creation time. Whenever an inner function references identifiers from an enclosing scope, JavaScript preserves that lexical environment on the heap rather than popping it from memory with the call stack.


Why are closures useful in web development?

Closures solve fundamental software engineering challenges in JavaScript by enabling state retention and encapsulation:

1. Data Privacy and Encapsulation

JavaScript lacked native private class fields for decades. Closures provided the standard pattern to hide implementation details and prevent unauthorized external mutation:

function createBankAccount(initialBalance) {
  let balance = initialBalance; // Private variable hidden by closure

  return {
    deposit(amount) {
      if (amount > 0) balance += amount;
      return balance;
    },
    getBalance() {
      return balance;
    }
  };
}

const myAccount = createBankAccount(100);
myAccount.deposit(50);
console.log(myAccount.getBalance()); // 150
console.log(myAccount.balance);     // undefined (Private!)

2. Function Factories and Currying

Closures allow you to author factory functions that generate customized functions tailored with preset configuration parameters:

function createMultiplier(multiplier) {
  return function(number) {
    return number * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

How do closures interact with the Event Loop?

A classic JavaScript interview puzzle demonstrates how closures interact with asynchronous execution and variable scoping:

// Problematic code with var:
for (var i = 1; i <= 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Prints: 4, 4, 4 (because var is function-scoped and shared)

// Solved with block-scoped let:
for (let j = 1; j <= 3; j++) {
  setTimeout(() => console.log(j), 100);
}
// Prints: 1, 2, 3 (each iteration creates a new lexical closure binding)

When using let, JavaScript creates a new lexical scope binding for each loop iteration, ensuring each timer callback closes over its own distinct value of j.


Common Mistakes with JavaScript Closures

  1. Unintentional memory retention (Memory Leaks): Retaining closures inside global caches, event listeners, or long-lived intervals prevents the garbage collector from freeing memory for referenced variables.
  2. Confusing closures with object copies: Closures capture references to live variables, not static snapshots of values. Mutating an enclosed variable alters the value for all functions sharing that closure.
  3. Overusing closures where pure functions suffice: Creating unnecessary closures in high-frequency loops creates extra function allocations and memory pressure.

Frequently Asked Questions About JavaScript Closures (with Real Examples)

What is a closure in JavaScript in simple terms?

A closure is when an inner function remembers and continues to access variables from its parent function, even after the parent function has finished executing.

Are closures bad for memory and garbage collection?

Closures prevent garbage collection of enclosed variables as long as the closure function itself remains reachable in memory. While not inherently bad, holding closures in global variables unnecessarily can cause memory leaks.

How do closures enable private variables in JavaScript?

By declaring variables with let or const inside an outer function and exposing only inner methods (getters/setters), external code cannot access or modify the internal variables directly.

Why does using let in a for loop fix the closure setTimeout issue?

Because let is block-scoped, JavaScript creates a fresh binding for the loop variable on every single iteration, allowing each timer callback to close over its own unique iteration value.

Related Runnable JavaScript Examples

Ready to test what you learned?

The best way to solidify your JavaScript skills is by writing and running code. Open our free online compiler and test your scripts with real-time console feedback.

Open JavaScript Compiler