A closure is a function that remembers the variables from the place it was created, even after that outer code has already finished running. Every function you write in JavaScript is technically a closure over its surrounding scope; most just do not happen to reference anything outside themselves, so the behavior stays invisible. Once a function does reach outside itself, and gets returned or passed somewhere else, that memory is what makes private variables, function factories, and a lot of everyday patterns possible.
What changed in 2026
- Framework docs now teach closures before hooks. React's official material and most 2026 bootcamps introduce closures explicitly before
useState and useEffect, because those hooks are close to impossible to reason about without understanding what a function remembers.
- AI assistants explain closures well but still generate closure bugs. Paste a specific closure problem into an assistant and the explanation is usually solid, but the same assistant will still generate a
setTimeout or event-handler callback that captures a stale value. Read anything generated that reaches outside its own scope.
- Modern bundlers optimize closures aggressively. Tools like esbuild and swc make the old advice to avoid closures in hot paths for performance reasons matter far less in 2026 than it did a decade ago.
The basic idea
function makeGreeter(name) {
const greeting = "Hello, " + name;
return function () {
console.log(greeting); // remembers `greeting`
};
}
const greetSam = makeGreeter("Sam");
greetSam(); // "Hello, Sam" — runs long after makeGreeter finished
greetSam still has access to greeting, even though makeGreeter already returned. The inner function carries its surrounding scope along with it, like a backpack it never puts down.
Using closures for private data
function createAccount(startingBalance) {
let balance = startingBalance; // not reachable from outside directly
return {
deposit(amount) { balance += amount; return balance; },
withdraw(amount) { balance -= amount; return balance; },
getBalance() { return balance; },
};
}
const account = createAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
balance only exists inside the closure. Nothing outside createAccount can touch it directly, which is close to a private field, without using the newer #private class syntax.
Function factories
function makeMultiplier(factor) {
return (n) => n * factor;
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
double(5); // 10
triple(5); // 15
Each call to makeMultiplier creates a fresh, independent factor. double and triple do not interfere with each other, because each closure has its own copy of the surrounding scope.
Where closures show up without you naming them
| Everyday pattern |
Where the closure is |
| An event listener set up with a config value |
The handler closes over the config passed to the setup function |
useState in React |
The setter and the current value are tied together by a closure |
A setTimeout callback using an outer variable |
The callback closes over whatever it references from the surrounding scope |
| A module that exposes only a few functions |
The private internals are closed over by the exposed functions |
Common mistakes
A closure does not copy a variable's value at creation time; it keeps a live reference, so if the outer variable changes later, the closure sees the new value. Capturing more than needed, such as holding onto a large object through a closure, keeps that object in memory as long as the closure exists, even if only one small piece of it was actually needed. A var declared inside a loop behaves surprisingly inside a closure for a related reason: var is hoisted to function scope rather than block scope, so every closure created in the loop shares the same variable. See what hoisting is in JavaScript for why var behaves this way; switching to let is usually the fix.
FAQ
Do I have to nest functions to get a closure?
An inner function needs to reference a variable from an enclosing scope, which in practice almost always means some form of nesting, whether a named function, an arrow function, or a callback.
Are closures unique to JavaScript?
No. Python, Rust, Swift, and many other languages have closures. The core idea, a function that remembers its defining scope, is the same everywhere, though syntax and rules differ.
Do closures cause memory leaks?
They can keep variables alive longer than expected if the closure itself is kept alive, such as an event listener that is never removed. It is a real consideration, though rarely a practical problem for typical application code.
Is an arrow function a closure?
An arrow function is a closure whenever it references a variable from its surrounding scope, exactly like a regular function. The arrow syntax changes how this is handled, not the closure behavior itself.
Where to go next