Closure in JavaScript: a function that remembers its environment

A closure is a function that keeps access to the variables of the place where it was created, long after that code has finished running.
3 min read
Believemy logo

Some functions carry a memory with them. Declared inside another function, a function keeps seeing its neighbor's variables long after that neighbor has finished running, as if it took a piece of the scenery along.

The mechanism has a name, the closure, and it explains much of what feels mysterious in JavaScript: counters that hold on to their value, genuinely private data without a class, and callbacks that print an unexpected number.


Definition

Move a single line, and the counters stop being independent
function createCounter() {
let total = 0;
return () => (total += 1);
}
counterA
7 calls
7
counterB
1 call
1
8 calls in total, spread between counters: 6

The counters come out of the same factory, yet their values are 6 apart. Each one keeps its own total variable, alive long after createCounter() returned.

Each row shows the value of the total variable that this counter reads, not the last value it returned: that is what makes a never-called counter visible. Switching versions replays the same sequence of calls against the other code without resetting anything; in a real program you would have to reload the page to change implementation. The bars are scaled to 12 calls, then to the total once it goes past 12, and the exact values are printed next to them.

Create several counters and call them. Each keeps its own count although they all come from the same code. Then switch to the shared variable to see the difference.

A closure is the pairing of a function with the environment in which it was declared. As long as the function exists somewhere, the variables of that environment stay alive, even though the code that created them has finished.

JAVASCRIPT
function createCounter() {
  let total = 0;

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

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(createCounter()()); // 1, a separate environment

The total variable should vanish when createCounter() returns. It survives because the returned function still references it. And every call to createCounter() builds a brand new environment: two counters never step on each other.


What it buys you in practice

  • Private state. total is reachable by no other code: no reading, no writing, no deleting.
  • A function factory. A function that returns another one, preconfigured with the values it received.
  • Memory between two calls. That is exactly how Memoization works, keeping its cache inside the closure.
  • An isolated module. Historically the job of the IIFE (immediately invoked function expression), before modules took over.
JAVASCRIPT
function multiplyBy(factor) {
  return (value) => value * factor;
}

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

console.log(double(7)); // 14
console.log(triple(7)); // 21


The loop trap

This is the most common closure-related bug, and it comes down entirely to the Scope of the loop variable.

JAVASCRIPT
for (var i = 1; i <= 3; i++) {
  setTimeout(() => console.log("var", i), 0);
}
// var 4, var 4, var 4

for (let j = 1; j <= 3; j++) {
  setTimeout(() => console.log("let", j), 0);
}
// let 1, let 2, let 3

With var there is a single variable for the whole loop: the three functions share it and read its final value. With let, every iteration gets its own variable, and therefore its own closure.

Good to know

This trap is the most concrete reason to prefer let over var inside a loop, well ahead of any style argument.


Frequently asked questions

Question

Does a closure keep the whole memory of the parent function?

No, modern engines only retain the variables the inner function actually references. Everything else is released as usual. Memory leaks happen when a closure lives a long time, for instance attached to a listener that is never removed, and holds a large object along the way.


Question

How is this different from an object holding the same data?

An object exposes its properties: any code can read and overwrite them. A closure only lets through what you explicitly return. It is the one truly airtight form of encapsulation in the language, alongside the private fields of a class written with a hash sign.


Question

Should closures be avoided for performance reasons?

No. Creating a closure costs roughly as much as creating an object, which is negligible in the vast majority of cases. The only scenario worth watching is creating thousands of functions inside a tight loop. Our JavaScript course comes back to this mechanism when covering callbacks, where it becomes unavoidable.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.