DEV Community

Ansh Sheladiya
Ansh Sheladiya

Posted on

Understanding JavaScript Closures: How They Work and Why They Matter

JavaScript closures are one of the most important concepts for understanding how functions work beyond their immediate scope. They allow a function to remember and access variables from the scope where it was created, even after that outer function has finished executing.

Closures appear throughout modern JavaScript, including callbacks, event handlers, factory functions, modules, and asynchronous code. Once you understand how lexical scope and closures work together, many JavaScript patterns become easier to read, design, and debug.

How JavaScript Closures Work

A closure is created when a function is defined inside another function and keeps access to variables from its surrounding lexical scope. The inner function does not copy those variables; instead, it maintains access to the environment where those variables were originally defined.

This behavior is useful for creating private state without exposing variables directly. In the example below, createCounter returns an object whose methods form closures over the private count variable, so the value can only be changed through the provided methods.

Closures are also useful when creating multiple independent instances of state. Each call to createCounter creates a new lexical environment, meaning one counter can be updated without affecting another counter.

The same principle applies to asynchronous callbacks and event handlers. JavaScript preserves the required lexical environment so that a callback can still access variables that existed when the callback was created.

function createCounter(start = 0) {
  let count = start;

  function increment() {
    count += 1;
    return count;
  }

  function decrement() {
    count -= 1;
    return count;
  }

  function getValue() {
    return count;
  }

  return {
    increment,
    decrement,
    getValue
  };
}

const firstCounter = createCounter(10);
const secondCounter = createCounter(100);

console.log(firstCounter.getValue());
console.log(firstCounter.increment());
console.log(firstCounter.increment());
console.log(firstCounter.decrement());

console.log(secondCounter.getValue());
console.log(secondCounter.increment());

function createGreeter(name) {
  const message = `Hello, ${name}!`;

  return function greet() {
    return message;
  };
}

const greetAnsh = createGreeter("Ansh");
console.log(greetAnsh());
Enter fullscreen mode Exit fullscreen mode

Conclusion

Closures give JavaScript functions the ability to retain access to variables from their original lexical environment. This makes them especially valuable for encapsulating state, building reusable functions, and controlling how data can be accessed or modified.

A strong understanding of closures also helps explain common JavaScript behavior involving callbacks, asynchronous operations, and function factories. Rather than treating closures as a complicated language feature, think of them as functions carrying access to the scope they were created in.

When writing JavaScript, use closures deliberately when private state or preserved context makes the design clearer. Understanding their behavior will make it easier to work with both everyday JavaScript code and larger applications built around functional patterns.

Top comments (0)