Skip to main content

JavaScript Scope & Hoisting

JavaScript Closures

Written by Published

A JavaScript closure is a function that remembers the variables around it, even after the outer function has finished.

The inner function keeps a live link to the scope it was created in.

This is how a function can hold private state between calls.

Example

Example

javascript

function makeCounter() {
  let count = 0;

  return function () {
    count++;
    return count;
  };
}

const counter = makeCounter();

console.log(counter());
console.log(counter());

The output is 1 then 2.

count survives even though makeCounter has already returned.

What is a Closure?

A closure is created whenever a function is defined inside another function.

The inner function carries the outer scope with it wherever it goes.

Nothing special has to be written: closures happen automatically because of how scope works.

Syntax

Syntax

javascript

function outer() {
  let value = 0;

  return function inner() {
    return value;
  };
}

inner can still read value after outer has finished.

Private Data

A closure is the normal way to keep a value that nothing else can reach.

The variable is not a property, so no outside code can read or change it directly.

Example

Example

javascript

function createAccount(start) {
  let balance = start;

  return {
    deposit: function (amount) {
      balance += amount;
      return balance;
    },
    getBalance: function () {
      return balance;
    }
  };
}

const account = createAccount(100);

console.log(account.deposit(50));
console.log(account.getBalance());

balance can only be changed through the two functions provided.

Function Factories

A closure lets you build specialised functions from a general one.

Example

Example

javascript

function multiplyBy(factor) {
  return function (n) {
    return n * factor;
  };
}

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

console.log(double(5));
console.log(triple(5));

Each returned function remembers its own factor.

Each Call Gets Its Own Scope

Calling the outer function again creates a completely separate closure.

Example

Example

javascript

function makeCounter() {
  let count = 0;
  return function () {
    count++;
    return count;
  };
}

const a = makeCounter();
const b = makeCounter();

console.log(a());
console.log(a());
console.log(b());

a reaches 2 while b is still at 1; they do not share count.

Closures in Loops

This is the clearest everyday example of closures at work.

With let, each turn creates a new binding for the closure to capture.

Example

Example

javascript

const functions = [];

for (let i = 1; i <= 3; i++) {
  functions.push(function () {
    return i;
  });
}

console.log(functions[0]() + "," + functions[1]() + "," + functions[2]());

The output is 1,2,3.

With var all three would return 4, because they would share one variable.

Closures Hold Memory

A closure keeps its outer variables alive for as long as the inner function exists.

That is usually what you want, but holding many large closures can use more memory than expected.

Example

Example

javascript

function once(fn) {
  let called = false;
  let result;

  return function (...args) {
    if (!called) {
      called = true;
      result = fn(...args);
    }

    return result;
  };
}

const setup = once(() => "ready");

console.log(setup());
console.log(setup());

Here the closure remembers whether the function has already run.

Complete Example

Complete Example

html

<!DOCTYPE html>
<html>
<head>
  <title>JavaScript Closures</title>
</head>
<body>

  <h1>JavaScript Closures</h1>

  <p id="result"></p>

  <script>
    function makeCounter() {
      let count = 0;

      return function () {
        count++;
        return count;
      };
    }

    const counter = makeCounter();

    document.getElementById("result").innerHTML =
      counter() + ", " + counter() + ", " + counter();
  </script>

</body>
</html>

Try It Yourself

Run the above example in the Try It Editor.

Try a second counter:

Create another counter with makeCounter() and confirm it starts again at 1.

Important Points

  • A closure is a function that remembers the scope it was created in.
  • The remembered variables survive after the outer function returns.
  • Closures are how JavaScript keeps private data.
  • Each call to the outer function creates a separate closure.
  • Closures keep their variables in memory for as long as they exist.

Conclusion

JavaScript closures let a function carry its surrounding scope with it.

They power counters, private data, function factories and much of the async code you will meet later.

Understanding closures is one of the biggest steps from beginner to confident JavaScript.