Keep Access to an Outer Binding
Keep Access to an Outer Binding
A closure is a function together with access to its surrounding lexical bindings. A school rule factory can return a function that remembers a particular threshold after the outer call returns.
Before this topic, review Understand Declarations Before Execution if its terms are unfamiliar.
What you’ll learn
- Explain keep access to an outer binding through the school example.
- Trace the shown operations and distinguish their result from displayed output.
- Make the practice change and explain its consequence: The returned function now compares marks against its retained threshold of 75.
Work through the school example
This is JavaScript using language features and the host console. Read the initial values, then follow each operation to its displayed result. A declaration names a value, a function call performs an operation, and console.log displays its arguments.
JavaScript · school.js · complete example
function rule(threshold) {
return (mark) => mark >= threshold;
}
const passed = rule(50);
console.log(passed(72));
Expected output
trueRun in JavaScript compiler →A rule retains its threshold
JavaScript · source for this model
function rule(threshold) {
return (mark) => mark >= threshold;
}
const passed = rule(50);
console.log(passed(72));
This diagram follows the supplied example. It is an explanatory model, not a JavaScript engine or a timing guarantee.
A rule retains its threshold
Call the factory
rule receives threshold 50.
50Return the function
The returned function retains access to the threshold binding.
passed → thresholdCall the result
passed receives 72 and compares it against 50.
true
What is a Closure in JavaScript?
A closure in JavaScript is a powerful concept that allows an inner function to access the variables of its outer function, even after the outer function has finished executing. This behavior is made possible through JavaScript's lexical scoping rules.
Understanding Lexical Scope
Lexical scope means that a function's ability to access variables is determined by its position in the source code. In other words, inner functions have access to the scope in which they were defined—not necessarily the scope from which they are called.
function outer() {
let outerVar = "I am from outer";
function inner() {
console.log(outerVar);
}
return inner;
}
const closureFn = outer();
closureFn();
I am from outer
How Closures Work
When outer() is called, it returns the inner function. Even though outer() has finished execution, its variable outerVar is still accessible to inner()—this is the essence of a closure.
Closures Retain State
Closures can maintain their own private state because they retain access to the variables in their lexical scope.
function counter() {
let count = 0;
return function() {
count++;
return count;
}
}
const increment = counter();
console.log(increment()); // 1
console.log(increment()); // 2
console.log(increment()); // 3
1
2
3
Each time increment() is called, it accesses and modifies the same count variable inside counter(). That variable is preserved due to closure.
Real-World Use Case: Data Privacy
Closures are useful when you want to encapsulate data and prevent it from being accessed directly.
function secretHolder(secret) {
return {
getSecret: function() {
return secret;
},
changeSecret: function(newSecret) {
secret = newSecret;
}
};
}
const obj = secretHolder("hidden123");
console.log(obj.getSecret()); // hidden123
obj.changeSecret("exposed456");
console.log(obj.getSecret()); // exposed456
hidden123
exposed456
The variable secret is not accessible from outside the function—it’s only accessible via the closure methods.
Closures Inside Loops – A Common Pitfall
Using closures in loops often leads to unexpected behavior. Let’s see why:
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
3
3
3
Because var is function-scoped, the variable i retains its final value of 3 in each closure. To fix this, use let or an IIFE:
Fix using let:
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
0
1
2
Fix using an IIFE (Immediately Invoked Function Expression):
for (var i = 0; i < 3; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
0
1
2
Closures in Event Handlers
Closures allow each event handler to remember its associated data.
function setupButtons() {
for (let i = 0; i < 3; i++) {
const btn = document.createElement('button');
btn.textContent = 'Button ' + i;
btn.onclick = function() {
alert('Clicked button ' + i);
};
document.body.appendChild(btn);
}
}
setupButtons();
Each button, thanks to the closure, correctly remembers its own i value when clicked.
Advanced Example: Partial Application
Closures can be used to create specialized versions of functions with some arguments preset.
function multiply(a) {
return function(b) {
return a * b;
};
}
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
10
15
Summary: Why Are Closures Important?
- They enable data encapsulation and information hiding.
- They help preserve state in asynchronous code and event handling.
- They’re fundamental to functional programming patterns in JavaScript.
Frequently Asked Questions
Is a closure the same as a function?
Not quite. Every closure is a function, but not every function creates a closure. A closure is formed when an inner function “remembers” variables from its outer lexical scope.
Are closures bad for performance?
Closures use more memory since variables must be retained in the scope chain. But this is usually negligible unless closures are heavily created in performance-critical code.
Can closures be garbage collected?
Yes, as long as there are no more references to the closure or the variables it closes over, they will be garbage collected.
Next Steps
Mastering closures will significantly level up your JavaScript skills. You’ll see closures behind the scenes in libraries, callbacks, event handlers, and more. Practice writing your own closures and observe how scope is preserved!
Recognize the important boundary
Closures retain access to bindings, not automatically frozen snapshots. Avoid retaining large objects longer than necessary through unused callbacks.
Try a small variation
JavaScript · original source for practice
function rule(threshold) {
return (mark) => mark >= threshold;
}
const passed = rule(50);
console.log(passed(72));
Your task: Replace rule(50) with rule(75). Predict the result before running the edited program.
The source is repeated here. If the edit targets the HTML rather than the script, use the markup repeated below; the worked answer also repeats the changed markup.
Worked answer and reasoning
The returned function now compares marks against its retained threshold of 75.
JavaScript · complete worked variation
function rule(threshold) {
return (mark) => mark >= threshold;
}
const passed = rule(75);
console.log(passed(72));
Expected output
falseRun in JavaScript compiler →Predict the original program
JavaScript · original program for this question
function rule(threshold) {
return (mark) => mark >= threshold;
}
const passed = rule(50);
console.log(passed(72));
Check the important distinction
Keep the useful idea
A closure is a function together with access to its surrounding lexical bindings. A school rule factory can return a function that remembers a particular threshold after the outer call returns.
Check your variation against this explanation: The returned function now compares marks against its retained threshold of 75.
Use the JavaScript array reference library for individual array operations and boundary tests to check a rule independently.
Reaching the recap and completing the quizzes are separate milestones.