Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstalliTechGuides 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 closure lets a function keep using bindings from the scope where it was created, even after that surrounding function has returned. For example, makeCounter() can return a function that increments a local count each time you call it: the returned function still has access to that count.
How lexical scope explains closures
JavaScript resolves a function’s variable names according to where the function was defined, not where it is later called. This rule is lexical scoping. A function nested inside another function can refer to bindings in the outer function’s scope.
For instance, inner can read message because it was defined inside the scope where message exists:
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsfunction outer() {
const message = "Hello";
function inner() {
return message;
}
return inner;
}
const sayHello = outer();
console.log(sayHello()); // "Hello"
Although outer() has finished running, sayHello() can still resolve message. The function and its access to relevant surrounding state form a closure. MDN describes a closure as “the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).” See the MDN JavaScript Guide to closures.
#1 Best Overall
A lexical environment is the surrounding context in which names are defined and resolved. A closure is not simply an anonymous function, nor is it limited to functions returned by other functions. A nested function or callback can use outer bindings whenever lexical scope makes them available.
Closures retain access to bindings, not frozen snapshots
It is tempting to imagine that a closure copies the value of every outer variable when the function is created. That is not the right mental model: a closure retains access to bindings it can resolve. If the binding changes, a later call can observe the changed value.
Rank #2
function makeReader() {
let status = "waiting";
return {
read() {
return status;
},
update(nextStatus) {
status = nextStatus;
}
};
}
const task = makeReader();
console.log(task.read()); // "waiting"
task.update("done");
console.log(task.read()); // "done"
Both methods refer to the same status binding created by one call to makeReader(). That shared access is useful when a function needs to read or update state later.
Function factories create separate environments
A function factory returns functions configured by the values in its own call. MDN’s makeAdder pattern demonstrates this:
function makeAdder(x) {
return function (y) {
return x + y;
};
}
const add5 = makeAdder(5);
const add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
Each call to makeAdder creates a distinct environment for its parameter x. The functions share the same function-body structure, but add5 resolves x from the call that supplied 5, while add10 resolves it from the call that supplied 10.
Closures can expose operations over private state
A closure can keep a local binding out of direct reach while returning functions that operate on it. This supports a small counter interface:
Rank #4
function createCounter() {
let count = 0;
return {
increment() {
count += 1;
return count;
},
current() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.current()); // 1
The methods share the environment created by that particular call to createCounter(). Code using counter can call the provided operations, but it cannot directly access the local count binding.
Why closures matter in callbacks
Callbacks often run later than the code that creates them. If a callback refers to an outer binding, lexical scope determines which binding it can use when it eventually runs. That is why closures are useful for callbacks as well as function factories and stateful interfaces.
Best Value
When a callback produces an unexpected value, trace the function back to where it was defined. The place where it is invoked does not change which outer binding its names resolve to.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.The classic loop closure problem: var versus let
var is function-scoped (or global when declared at top level), not block-scoped. In a loop, callbacks created with a var loop variable can all refer to the same binding. If those callbacks run after the loop ends, they observe that binding’s final value rather than a separate value for each iteration.
const callbacks = [];
for (var i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
console.log(callbacks[0]()); // 3
console.log(callbacks[1]()); // 3
console.log(callbacks[2]()); // 3
Here, each callback resolves the same function-scoped i. A loop declaration with let provides a per-iteration binding, so each callback can use its iteration’s value:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →const callbacks = [];
for (let i = 0; i < 3; i++) {
callbacks.push(function () {
return i;
});
}
console.log(callbacks[0]()); // 0
console.log(callbacks[1]()); // 1
console.log(callbacks[2]()); // 2
Other modern alternatives include iterating with for...of or using forEach(), where the callback receives an iteration value. MDN explains the loop behavior and these alternatives in its closures guide.
Closure cheat sheet
- Definition: a function together with access to relevant references in its surrounding lexical environment.
- Scope rule: names resolve according to where a function is defined, not where it is called.
- After a return: an inner function can still use outer bindings it refers to.
- Separate factory calls: each call can create a separate environment, as with
makeAdder(5)andmakeAdder(10). - Shared state: functions created in one environment can share access to the same binding.
- Loop caution: callbacks in a
varloop can share one binding;letloop declarations create per-iteration bindings.
For debugging, ask: Which binding does this function resolve here? Then locate where the function was defined and check whether other functions share or modify that binding.
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.

