What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

A JavaScript closure is a function that retains access to names in the lexical environment where it was created. That lets a function use surrounding variables later—even after the outer function has returned. This guide uses JavaScript throughout; other languages use different syntax and may define capture behavior differently.

How can a function remember a variable?

JavaScript resolves a name according to where code is written, not where a function is eventually called. This is called lexical scoping. A closure is a function together with access to its surrounding lexical environment, as described in MDN’s JavaScript closures guide and its closure glossary entry.

Start with an inner function that reads a variable from the function around it:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function makeGreeter(name) {
  function greet() {
    return `Hello, ${name}!`;
  }

  return greet;
}

const greetMina = makeGreeter("Mina");
console.log(greetMina()); // Hello, Mina!

makeGreeter assigns a parameter named name, then returns greet. The call to makeGreeter finishes before greetMina() runs, but the returned function can still resolve name from the environment in which it was created. That continuing access is the closure.

The closure is not special punctuation or a separate kind of function. It describes the relationship between a function and the surrounding environment it can access.

What does a closure capture: a value or a binding?

It is more useful to think of a closure as retaining access to a binding—the named place a variable refers to—than as automatically taking a frozen copy of its value. If the binding changes and the closure can still access it, a later call can observe the updated value.

function makeCounter() {
  let count = 0;

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

const nextCount = makeCounter();
console.log(nextCount()); // 1
console.log(nextCount()); // 2

The returned function can both read and update count. Each call to makeCounter() creates a separate environment, so separate counters have separate bindings:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const first = makeCounter();
const second = makeCounter();

first();
console.log(first());  // 2
console.log(second()); // 1

Closures do not all behave as though they copy values at creation time. The result depends on the language’s capture rules and on whether the surrounding binding is changed. In Swift, for example, the official language guide describes closures as self-contained blocks that can be passed around and that can capture constants and variables from their defining context; its terminology and syntax differ from JavaScript. See Swift’s closures guide.

Where are closures useful?

Creating specialized functions

A function factory returns functions configured with different values. MDN illustrates this with a makeAdder(x) pattern: each returned function can add the particular x supplied when the factory was called.

function makeAdder(amount) {
  return function (number) {
    return number + amount;
  };
}

const addFive = makeAdder(5);
const addTen = makeAdder(10);

console.log(addFive(3)); // 8
console.log(addTen(3));  // 13

addFive and addTen use the same function logic but retain access to different amount bindings.

Keeping state behind functions

The counter example uses a closure to keep count out of the surrounding code’s direct reach while exposing an operation that updates it. A closure can therefore help associate state with functions that act on it. This can resemble an object with a method, but closures and objects are not interchangeable in every design.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handling callbacks that run later

A callback is a function passed to other code to be called later—for example, when an event occurs. If the callback needs a surrounding variable, a closure lets it retain access to that context after the code that registered it has continued.

function registerGreeting(button, name) {
  button.addEventListener("click", function () {
    console.log(`Hello, ${name}!`);
  });
}

The event handler runs when the button is clicked, not when registerGreeting creates it. Its closure gives it access to the name binding from that call.

Why can callbacks in a JavaScript loop show the wrong value?

A classic JavaScript pitfall occurs when a loop declares its counter with var and registers callbacks that read it. Because var is function-scoped rather than block-scoped, the callbacks can share one binding. If they run after the loop finishes, they may all read the final value associated with that binding—not separate values from each iteration.

for (var i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}

In this example, the callbacks run after the loop has advanced i to 3, so each logs 3. The issue is not that closures always capture a final value: these callbacks share a single changing, function-scoped binding.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For this loop pattern, declaring the counter with let gives each iteration its own block-scoped binding:

for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 0);
}

These callbacks log 0, 1, and 2. MDN’s closures guide also shows a function-factory approach that creates a separate environment for each callback; the right fix depends on the code’s purpose.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How can you recognize a closure?

  • A function refers to a name declared outside its own body.
  • That function is passed elsewhere, returned, or called after the surrounding code has continued.
  • The function still needs that surrounding binding when it runs.

When tracing a confusing example, identify where the function was written, list the outer names it refers to, and check whether those bindings are shared or changed before the function runs. That separates lexical scope from the later call site and helps explain what the closure can observe.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.