A JavaScript closure lets a function keep using bindings from the scope where it was defined, even after that surrounding function has returned. This is why returned functions can retain different values, callbacks can carry state, and a group of methods can work with data callers cannot access directly.
What is a closure?
JavaScript uses lexical scope: when a function refers to a name that is not defined inside it, the language resolves that name by looking through the scopes surrounding the function’s definition. A closure is a function together with references to the surrounding lexical environment it needs. The function’s definition site—not the place where it is later called—determines which outer bindings it can access. MDN’s guide to closures explains the concept; ECMA-262 describes lexical environments as the specification mechanism that associates identifiers with variables and functions according to lexical nesting.
As an Amazon Associate I earn from qualifying purchases.
For example, an inner function can read a variable declared in an outer function. If the inner function is returned and called later, it can still use that outer binding. Returning the function does not sever its connection to the environment.
How a returned function retains a value
A function factory makes this behavior easy to see: it creates and returns a function customized with a value from the factory’s call.
#1 Best Overall
function makeAdder(x) {
return function (y) {
return x + y;
};
}
const addFive = makeAdder(5);
const addTen = makeAdder(10);
addFive(2); // 7
addTen(2); // 12
Each call to makeAdder creates a returned function associated with its own surrounding environment. The function produced by makeAdder(5) uses the x binding set to 5; the one produced by makeAdder(10) uses a separate x binding set to 10. The outer calls have finished, but the returned functions can still read the bindings they use. MDN presents this function-factory pattern in its closure examples.
Where closures are useful
Callbacks that need surrounding values
A callback can refer to variables from the scope where it was created. This is useful when a callback runs later, such as an event handler that needs access to state or configuration established during setup. The callback does not need those values passed as new arguments at every invocation; it can use the lexical bindings it closes over.
Rank #2
Private state behind an interface
A function can create state and return a set of methods that share access to it. Callers can use the methods without directly reading or replacing the private bindings.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →function makeCounter() {
let count = 0;
function changeCount(amount) {
count += amount;
}
return {
increment() {
changeCount(1);
},
current() {
return count;
}
};
}
const counter = makeCounter();
counter.increment();
counter.current(); // 1
The returned methods share the lexical environment containing count and changeCount. The caller can ask for the current value or increment it, but cannot access the local count binding by name. This is one way to organize state, not a requirement for every JavaScript object; an object with methods is a familiar conceptual alternative.
Why callbacks in a var loop can surprise you
A closure retains access to a binding, not necessarily a frozen copy of the value the binding held when the function was created. This distinction explains a common loop mistake: callbacks created inside a loop using var refer to the same function-scoped loop variable.
const callbacks = [];
for (var i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
callbacks[0](); // 3
callbacks[1](); // 3
callbacks[2](); // 3
When these callbacks are invoked after the loop, each reads the shared i binding, which has reached 3. They do not each retain a separate iteration value.
Rank #4
In this pattern, changing var to let gives each loop iteration its own block-scoped binding, so each callback sees that iteration’s value:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
const callbacks = [];
for (let i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
callbacks[0](); // 0
callbacks[1](); // 1
callbacks[2](); // 2
The difference matters when a callback needs a distinct value from each iteration. It does not mean every callback or loop using var is faulty; the issue is whether callbacks share a binding when separate per-iteration values were intended. MDN discusses this loop pattern and its let alternative.
Best Value
Do closures have a performance cost?
Closures can affect performance in some situations, but neither MDN’s guidance nor the cited ECMAScript specification supports a universal cost or a single numeric penalty. The specification’s lexical environments describe language behavior; they do not require a particular implementation structure. Actual implementation details can vary, so avoid assuming that every closure creates the same memory or speed impact. For most code, choose closures for clear scope and state management, then investigate performance only when a measured problem calls for it. MDN includes a performance considerations section.
Further learning
MDN’s JavaScript closures guide provides additional examples, including callbacks, private state, and common mistakes. For broader study, a JavaScript programming book can provide a structured path through the language alongside the free documentation.
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.




