Demystifying Closures in JavaScript.
Understanding closures in-depth is key when you're on your JS journey. This blog helps with just that!
Closures. If you are getting started with JS or are not sure you've mastered the nitty-gritty of closures, then you're in the right place.
Quickly, let's go through what the MDN docs have to say:
A closure is a combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
Let's break this definition down with a simple example.
function outerFunction() {
var someValue = 3;
return function innerFunction() {
console.log(someValue);
};
}
const thirdFunction = outerFunction();
thirdFunction();
Guess the output for a second before reading on.
You probably guessed it right, it's 3
! But maybe you're not too sure why.
How does innerFunction
access the variable someValue
which lies inside outerFunction
after outerFunction has been called and wiped from the call stack?!
innerFunction
seems to have access to the variable even after the execution context for outerFunction
has been lost along with its variables.
Closures make this possible.
Revisiting the MDN definition that says In JavaScript, closures are created every time a function is created, at function creation time, we realize that closures are formed each time we write a function.
Functions bundled with their lexical environment (the environment the function was created in) forms a closure.
When innerFunction
is returned, the closure that was formed with the function is returned with it. It is crucial here to realize that a long way down the same program, innerFunction
can be called which will return someValue
which it has remembered.
Covering some gotchas!
Let's make some changes to the above code:
function outerFunction() {
var someValue = 3;
function innerFunction() {
console.log(someValue);
}
someValue = 6; //re-declaring someValue as 6 to see what our function prints
return innerFunction;
}
const thirdFunction = outerFunction();
thirdFunction();
//output: 6
Here, we test out one case where someValue
gets redeclared before the function is returned.
Running the above code will print 6
not 3
. The reason here that is important to understand is the closure takes the references of the variables into consideration and not their values.
someValue
is remembered as 6
as its reference points to the value 6.
Declaring another parent function.
function outermostFunction() {
var anotherValue = 7;
function outerFunction() {
var someValue = 3;
function innerFunction() {
console.log(someValue, anotherValue);
}
innerFunction();
}
outerFunction();
}
outermostFunction();
//output: 3,7
Here, the innerFunction
is able to form a closure with the lexical scope extending all the way to the outermostFunction
and is able to bundle and remember someValue
as well as anotherValue
.
During function declaration, innerFunction
successfully retains ALL values inside its lexical scope, thereby making them usable when the function is called later.
Uses.
Closures make JS powerful and are super useful in implementing the following and many more:
- Currying.
Once
function.- Maintaining the state in asynchronous functions.
- Timers and Iterators.
- Memoization.
Put simply, closures are formed when a function gets bundled with its lexical environment, i.e every function forms a closure with references to its lexical environment, giving us access to an outer function's scope from an inner function.
thank you for reading!
Here are some references for you, should you want to dive deeper into closures:
What is a Closure? By Eric Elliot (Medium)
Closures in 100 Seconds by Fireship:
Deeper dive into closures by Akshay Saini: