JAVASCRIPT

JavaScript Closure: Syntax, Usage, and Examples

A JavaScript closure is a function that retains access to variables from its parent function, even after the parent function has finished executing. Closures let you maintain state, create function factories, and manage private variables, making them a powerful tool in JavaScript.

How to Use Closures in JavaScript

Closures occur when a function is declared inside another function and keeps a reference to its outer function’s variables.

function outerFunction(outerVariable) {
  return function innerFunction(innerVariable) {
    console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);
  };
}

const newFunction = outerFunction("Hello");
newFunction("World"); // Output: Outer: Hello, Inner: World
  • outerFunction returns innerFunction.
  • innerFunction remembers outerVariable, even after outerFunction has executed.

Closures work because of lexical scoping. When JavaScript executes a function, it first looks for variables inside that function. If it doesn't find them, it looks at the parent scope, continuing up until it reaches the global scope.

When to Use Closures in JavaScript

Closures shine when you need:

  1. Data Privacy – Encapsulate variables so they are not accessible outside a function.
  2. Function Factories – Create functions dynamically with preset values.
  3. Callbacks and Event Listeners – Retain context for asynchronous operations.
  4. Memoization and Caching – Store values for better performance.
  5. Module Pattern – Simulate private variables in JavaScript.

Examples of Closures in JavaScript

Creating Private Variables

If you want to prevent direct access to a variable but still modify it through controlled functions, closures help create private variables.

function counter() {
  let count = 0;
  return {
    increment: function () {
      count++;
      console.log(`Count: ${count}`);
    },
    decrement: function () {
      count--;
      console.log(`Count: ${count}`);
    },
    getCount: function () {
      return count;
    },
  };
}

const myCounter = counter();
myCounter.increment(); // Count: 1
myCounter.increment(); // Count: 2
console.log(myCounter.getCount()); // 2
myCounter.decrement(); // Count: 1

In this example, count is private because it exists inside the counter function. You can only modify it through the increment, decrement, and getCount methods.

Function Factories

Closures allow you to generate functions dynamically with specific behaviors.

javascript
CopyEdit
function multiplier(factor) {
  return function (number) {
    return number * factor;
  };
}

const double = multiplier(2);
console.log(double(5)); // Output: 10

const triple = multiplier(3);
console.log(triple(5)); // Output: 15

Each time you call multiplier, it creates a new function with its own factor value.

Closures in Asynchronous Code

Closures help retain values in asynchronous operations, such as event handlers and timers.

function delayedGreeting(name) {
  setTimeout(function () {
    console.log(`Hello, ${name}!`);
  }, 2000);
}

delayedGreeting("Alice"); // Output (after 2 sec): Hello, Alice!

Even after delayedGreeting has finished executing, the setTimeout function still has access to name.

Looping with Closures

If you’ve used var inside a loop, you may have encountered unexpected behavior:

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Output: 3, 3, 3
  }, 1000);
}

Since var is function-scoped, all callbacks reference the same i, which becomes 3 after the loop finishes.

To fix this, use let, which is block-scoped:

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // Output: 0, 1, 2
  }, 1000);
}

Another workaround is to use an IIFE (Immediately Invoked Function Expression) to create a new scope for each iteration:

for (var i = 0; i < 3; i++) {
  (function (i) {
    setTimeout(() => {
      console.log(i); // Output: 0, 1, 2
    }, 1000);
  })(i);
}

Learn More About Closures in JavaScript

Closures and Memory Management

Closures can cause memory leaks if they retain large variables unnecessarily. The garbage collector cannot free memory if there are lingering references.

function createClosure() {
  let largeArray = new Array(1000000).fill("data");
  return function () {
    console.log(largeArray.length);
  };
}

let closureFunction = createClosure();
closureFunction();

// Remove reference to allow garbage collection
closureFunction = null;

Releasing references ensures JavaScript can reclaim memory.

Closures in JavaScript Scope

Closures work because of JavaScript’s lexical scope. Functions have access to variables from their parent scope, even if the parent function has finished executing.

function outer() {
  let message = "I'm inside outer";
  function inner() {
    console.log(message);
  }
  return inner;
}

const closureExample = outer();
closureExample(); // Output: I'm inside outer

The inner function maintains access to message, even though outer has completed execution.

Closures in Async Programming

Closures help maintain state in asynchronous code, including promises and event listeners.

function fetchUserData(userId) {
  fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
    .then(response => response.json())
    .then(data => {
      console.log(`User: ${data.name}`);
    });
}

fetchUserData(1);

Even after fetchUserData runs, the .then() callback still accesses userId and data.

Avoiding Common Closure Pitfalls

  • Holding large objects in closures: Avoid keeping large arrays or objects inside closures unless necessary.
  • Unintended variable sharing: Each function call should create a new closure to prevent unintended behavior.
  • Closures inside loops: Use let or an IIFE to ensure each iteration gets a fresh copy of the variable.

JavaScript Closure Use Cases

  1. Encapsulation – Hide internal variables from direct access.
  2. Function Factories – Generate functions with preset parameters.
  3. Asynchronous Operations – Preserve data across delays or API calls.
  4. Event Listeners – Retain context for event-driven behavior.
  5. Memoization – Cache results to improve performance.

Closures are a fundamental concept in JavaScript. They let you manage scope effectively, create flexible functions, and optimize memory usage. By mastering closures, you’ll write more powerful and efficient JavaScript code.

Learn to Code in JavaScript for Free
Start learning now
button icon
To advance beyond this tutorial and learn JavaScript by doing, try the interactive experience of Mimo. Whether you're starting from scratch or brushing up your coding skills, Mimo helps you take your coding journey above and beyond.

Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.

You can code, too.

© 2025 Mimo GmbH