Understanding Closures in JavaScript

A clear, practical explanation of JavaScript closures, how they work under the hood, and why they are essential for writing reliable, scalable code.
A closure in JavaScript is created when a function remembers its lexical scope, even when the function is executed outside that scope. This allows functions to access variables from their outer scope long after the outer function has finished execution.
Closures are a direct result of JavaScript’s lexical scoping rules and are commonly used to build powerful abstractions, manage state, and create private variables.
Key idea: A closure gives you access to an outer function’s scope from an inner function.
Basic Closure Example
function outerFunction() {
let count = 0;
function innerFunction() {
count++;
console.log(count);
}
return innerFunction;
}
const counter = outerFunction();
counter(); // 1
counter(); // 2
Even though outerFunction has finished executing, the variable
count remains accessible because innerFunction
retains a reference to its lexical environment.
Why Closures Are Useful
- Data encapsulation and privacy
- State management without global variables
- Factory functions
- Callbacks and event handlers
Closures for Data Privacy
function createUser(name) {
let isLoggedIn = false;
return {
login() {
isLoggedIn = true;
console.log(name + " logged in");
},
logout() {
isLoggedIn = false;
console.log(name + " logged out");
},
};
}
const user = createUser("Sabbir");
user.login();
The variable isLoggedIn cannot be accessed directly from outside
the function, making closures a powerful tool for data hiding.
Common Closure Pitfall
for (var i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
This logs 4 three times because var is function-scoped.
The solution is to use let, which creates a new binding per
iteration.
for (let i = 1; i <= 3; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Conclusion
Closures are one of the most important concepts in JavaScript. Mastering them enables developers to write cleaner, more modular code and avoid many common pitfalls related to scope and state management.
Share this insight