A closure is one of the most powerful, yet often confusing, concepts in JavaScript. Simply put, a closure is the combination of a function bundled together with references to its surrounding state (the lexical environment).

In even simpler terms: A closure allows an inner function to access the scope of an outer function even after the outer function has finished executing.

1. The Foundation: Lexical Scoping

To understand closures, you first need to understand Lexical Scoping. This means that the JavaScript engine determines the scope of a variable based on where that variable is declared in the source code.

Nested functions have access to variables declared in their outer scope.

JavaScript

function outer() {
  let outerVar = "I am from the outside!";

  function inner() {
    console.log(outerVar); // Accesses variable from the parent scope
  }

  inner();
}

outer(); // Logs: "I am from the outside!"

2. What makes it a “Closure”?

A closure is created when an inner function is returned from an outer function. Even though the outer function has finished running (and its local variables would normally be deleted), the inner function “closes over” and preserves those variables.

The Step-by-Step Execution:

  1. Call the outer function: It creates a local variable.
  2. Return the inner function: Instead of running the inner function, the outer function hands the “blueprint” of the inner function back to you.
  3. The “Memory” remains: The returned function carries a hidden reference to the variables that existed when it was created.

Example:

function createGreeting(greeting) {
  return function(name) {
    console.log(greeting + ", " + name);
  };
}

const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");

sayHello("Alice"); // "Hello, Alice"
sayHi("Bob");      // "Hi, Bob"

In this case, sayHello is a closure. It remembers that greeting was “Hello” even though createGreeting has already finished executing.

3. Practical Use Case: Data Privacy

Closures are often used to create private variables. Since the outer variable isn’t accessible from the global scope, you can only change it using the inner function.

function createCounter() {
  let count = 0; // Private variable

  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    }
  };
}

const myCounter = createCounter();

console.log(myCounter.increment()); // 1
console.log(myCounter.increment()); // 2
console.log(myCounter.count);       // undefined (cannot access directly!)

4. Why are Closures Useful?

A. Data Privacy (Encapsulation)

You can use closures to create “private” variables that cannot be accessed or modified from the outside.

JavaScript

function createCounter() {
  let count = 0; // Private variable

  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    }
  };
}

const myCounter = createCounter();
console.log(myCounter.increment()); // 1
console.log(myCounter.increment()); // 2
// There is no way to access 'count' directly (e.g., myCounter.count is undefined)

B. Function Factories

As seen in the createGreeting example, you can create functions that are “pre-configured” with certain data.

5. Closures in Loops

Before let was introduced, closures in loops often caused bugs because the variable var was function-scoped, not block-scoped.

The Problem:

for (var i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i); // This will log '4' three times!
  }, 1000);
}

The Solution: Using let creates a new lexical scope for every iteration of the loop, effectively creating a unique closure for each step.

for (let i = 1; i <= 3; i++) {
  setTimeout(function() {
    console.log(i); // Logs 1, 2, 3 correctly
  }, 1000);
}

Categorized in:

Javascript,