Closures in JavaScript

In simple terms, a closure is a function that "remembers" its lexical scope, even when the function is executed outside that scope. This means that a function retains access to variables from its surrounding environment even after that environment has finished executing.
Lexical Scope
Before diving into closures, it’s essential to understand lexical scope. In JavaScript, scope refers to the context in which a variable is declared. Lexical scope means that a function's scope is determined by where it was declared, not where it is called.
function outerFunction() {
let outerVariable = 'I am from the outer function';
function innerFunction() {
console.log(outerVariable);
}
innerFunction();
}
outerFunction();
How Closures Work
Now that we understand lexical scope, let’s move on to closures. A closure occurs when an inner function remembers and can access variables from its outer function, even after the outer function has finished executing.
function outerFunction() {
let count = 0;
function innerFunction() {
count++;
console.log(count);
}
return innerFunction;
}
const closureExample = outerFunction();
closureExample(); // 1
closureExample(); // 2
closureExample(); // 3
Why Are Closures Important?
Closures allow for:
- Data encapsulation (private variables)
- Function factories
- Efficient memory usage
1. Data Encapsulation
function counter() {
let count = 0;
return {
increment: function() {
count++;
console.log(count);
},
decrement: function() {
count--;
console.log(count);
},
getCount: function() {
return count;
}
};
}
const myCounter = counter();
myCounter.increment(); // 1
myCounter.increment(); // 2
2. Function Factories
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
console.log(double(5)); // 10
3. Memory Efficiency
function createCounter() {
let count = 0;
return function() {
return count++;
};
}
const counter1 = createCounter();
console.log(counter1()); // 0
console.log(counter1()); // 1
Practical Use Cases
function startCountdown(seconds) {
let time = seconds;
const timer = setInterval(function() {
if (time >= 0) {
console.log(time);
time--;
} else {
clearInterval(timer);
console.log('Time is up!');
}
}, 1000);
}
startCountdown(3);
Event Listeners
function createButton() {
let clicks = 0;
const btn = document.createElement('button');
btn.textContent = 'Click Me';
btn.onclick = function() {
clicks++;
console.log(`Clicked ${clicks} times`);
};
document.body.appendChild(btn);
}
createButton();
Summary
Closures may seem abstract at first, but once you grasp their behavior and potential, they become a powerful tool in your JavaScript toolkit. Whether you’re managing private state, creating reusable logic, or working with asynchronous functions, closures help you write more modular and effective code.