A JavaScript closure lets a function keep using bindings from the scope where it was created—even after that outer function has finished. In the example below, makeGreeter() returns a function that still knows the value of name:
function makeGreeter(name) {
return function () {
return `Hello, ${name}!`;
};
}
const greet = makeGreeter("Mina");
console.log(greet()); // "Hello, Mina!"
The returned function works because its lexical environment remains available to it. To understand why, start with how JavaScript decides which variable a function uses.
As an Amazon Associate I earn from qualifying purchases.
How lexical scope determines a variable
JavaScript uses lexical scope: a function resolves a name based on where the function is defined in the source code, not on the place from which it is later called. If a function refers to a binding in an enclosing scope, it can access that binding.
const message = "outside";
function showMessage() {
console.log(message);
}
function run() {
const message = "inside";
showMessage();
}
run(); // "outside"
showMessage was defined in the scope containing the outer message, so calling it from run does not redirect its lookup to run‘s local binding. The chain of surrounding scopes associated with where a function is defined is its lexical environment.
#1 Best Overall
What a closure retains
MDN defines a closure as “the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).” In practice, a closure is a function together with access to the outer bindings it can resolve. A nested function can therefore use an outer binding after the outer function has returned.
A closure does not freeze or copy every outer variable as a snapshot. It retains access to bindings, so if a binding changes, a closure that reads it can observe the updated value.
function makeReader() {
let status = "waiting";
return function () {
return status;
};
}
const readStatus = makeReader();
console.log(readStatus()); // "waiting"
When makeReader returns, readStatus can still read the status binding from the environment where the inner function was created.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsRank #2
Function factories create separate environments
A function factory returns a function whose behavior depends on bindings established by that particular call. Calling the factory again creates a separate environment, even though each returned function uses the same function-body structure.
function makeAdder(x) {
return function (value) {
return value + x;
};
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
add5 resolves x in the environment created by makeAdder(5); add10 resolves it in the distinct environment created by makeAdder(10). This is why one factory can produce functions with different retained values.
Closures can share private state
When several functions are created within one invocation, they can share access to the same binding. This makes it possible to keep state inside a function and expose only operations that read or update it.
function makeCounter() {
let count = 0;
return {
increment() {
count += 1;
return count;
},
current() {
return count;
}
};
}
const counter = makeCounter();
console.log(counter.increment()); // 1
console.log(counter.current()); // 1
Both methods close over the same count binding. Code using counter can call the provided methods but cannot access count directly by that name.
Where closures appear in everyday JavaScript
- Callbacks: A callback can use bindings from the scope in which it was defined, even when another part of the program calls it later.
- Function factories: A factory can return specialized functions, as
makeAdderdoes. - State associated with operations: Multiple functions can share an environment that holds state, as the counter methods do.
- Nested functions: A nested function can read outer bindings whether or not it is returned.
Closures are not limited to anonymous functions or functions returned from other functions. The essential point is access to the function’s lexical environment.
Why callbacks in a var loop can all see the same value
var is function-scoped rather than block-scoped. In a loop that uses one var binding, callbacks created during the loop can all refer to that shared binding. If the callbacks run after the loop has finished, they can each read its final value.
Rank #4
const callbacks = [];
for (var i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
console.log(callbacks.map(callback => callback())); // [3, 3, 3]
The callbacks do not each retain a separate snapshot of i. They resolve the same function-scoped binding, whose value is 3 after the loop ends.
Use let for a per-iteration binding
A let declaration in a loop creates a separate binding for each iteration, so each callback can resolve the value from its own iteration.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →const callbacks = [];
for (let i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
console.log(callbacks.map(callback => callback())); // [0, 1, 2]
Use for...of when iterating values
If callbacks should use each item directly, for...of with a block-scoped binding is another clear option.
Best Value
const callbacks = [];
for (const value of ["a", "b", "c"]) {
callbacks.push(function () {
return value;
});
}
console.log(callbacks.map(callback => callback())); // ["a", "b", "c"]
MDN also documents forEach() as an alternative for this pattern. These are different ways of structuring the bindings callbacks use; choose the form that makes the iteration and intended values easiest to follow.
JavaScript closures cheat sheet
| Question | Quick answer |
|---|---|
| What is a closure? | A function together with access to bindings in its lexical environment. |
| Where does a function look for outer names? | In the scopes around where it was defined, not where it is called. |
| Does a closure copy a variable’s value? | No. It retains access to the binding the function resolves. |
| Can two closures share state? | Yes. Functions created in the same environment can access the same binding. |
Why can var loop callbacks return the same value? |
They may all resolve one shared function-scoped binding. |
| How can a loop give callbacks distinct values? | Use a block-scoped loop declaration such as let, or an iteration form such as for...of or forEach(). |
For a deeper reference, see MDN’s JavaScript guide to closures.
Quick Recap
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.




