What are closures, and how do they work?
Closures are one of the most powerful and often misunderstood features in JavaScript. At its core, a closure is a function that has access to variables in its outer enclosing lexical scope, even after the outer function has returned. This might sound complex but it’s a fundamental concept that enables many advanced programming patterns in JavaScript.
To understand closures, let’s start with an example:
function outerFunction(x) {
let y = 10;
function innerFunction(){
console.log(x + y);
}
return innerFunction;
}
let closure = outerFunction(5);
closure(); // Output: 15
In this example, outerFunction
takes a parameter x
and defines a local variable y
. It then defines an innerFunction
that uses both x
and y
. Finally it returns innerFunction
. When we call outerFunction(5)
it returns innerFunction
which we assign to the variable closure
. When we later call closure()
it still has access to x
and y
from outerFunction
even though outerFunction
has already finished executing. This is the essence of a closure.
The inner function maintains a reference to its outer lexical environment, preserving access to the variables in that environment even after the outer function has completed.
Closures are particularly useful for creating private variables and functions. Consider this example:
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
let counter = createCounter();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
In this case, createCounter
returns a function that increments and returns a count
variable. The count
variable is not directly accessible from outside createCounter
, but the returned function (our closure) has access to it. Each time we call counter()
, it increments and returns the count
.
Closures can also capture multiple variables from their outer scope. For example:
function multiply(x) {
return function (y) {
return x * y;
};
}
let double = multiply(2);
console.log(double(5)); // Output: 10
Here the inner function captures the x
parameter from multiply
. When we create double
by calling multiply(2)
it returns a function that always multiplies its argument by 2
.
One important thing to note about closures is that they capture variables, by reference not by value. This means if the value of a captured variable changes, the closure will see the new value. For example:
function createIncrementer() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
let increment = createIncrementer();
increment(); // Output: 1
increment(); // Output: 2
Each time we call increment
its working with the same count
variable, not a copy of it’s initial value. Closures are a powerful tool in JavaScript. as you continue to work with JavaScript you’ll find that understanding and using closures effectively can greatly enhance your ability to write clean, efficient and powerful code.