Understanding Closures in JavaScript
Let's bring closure to your doubts on closures
Introduction
If you’re familiar with functional programming, you would have heard of closures. They are one of the most important concepts in JavaScript. They are used to create private variables and functions. They are also used to create private methods and properties in JavaScript objects. But what exactly is it? Let’s have a look
Before we understand closures, we must understand what lexical scope is. It may seem confusing at first so let’s break it down.
What is Scope?
In simple terms scope
means where to look for things. So, what are we looking for? Well, we’re looking for identifiers (functions and variables).
Scope can be defined as the region or context in which values and expressions can be referenced.
Let’s take the analogy of sorting a bunch of coloured balls into bags. So, we are sorting marbles (identifiers) and we’re putting them in the correct bag (area of context or units of scope a.k.a functions and blocks).
That’s how the JavaScript engine parses the code, every time it encounters a function or a block it creates a scope (bag) and when it comes across an identifier (balls) it puts it into the right scope.
Alright, so what is lexical scope?
Lexical scope refers to the place the item was declared. Lexical scoping is also known as static scoping.
Consider the code below:
// Defining a variable in global scope
const greeting = "Eh, What's up doc?";
// Use the variable from a different scope
function getGreeting () {
return greeting;
}
In the above snippet, we have defined the variable greeting
in the global scope and called it in the function scope of getGreeting
.
Here the lexical scope of greeting
is the global scope because it is defined there.
This means that the variable greeting
is accessible inside the function getGreeting
because it is declared in the same scope as the function. This is called lexical scoping.
In lexical scope, a function scope is able to access variables or entities from the parent scope or up the scope chain as the function itself originates from that scope and so on.
Closures
Closures though they may seem to revolve around JavaScript actually exist in many languages. Closures is originally a mathematical concept from lambda calculus and predate programming in itself, I’m not going to get into the formulas but for any of you who thought where you’d use calculus, well here’s one more use case in that list. Many of us might have interacted or used closures in some way, shape or form while writing code without even realizing it.
What are Closures?
Closure is when a function remembers the variables around it (its lexical scope) even when the function is executed elsewhere (outside that lexical scope).
A closure has access to three scope chains:
- It has access to its own scope - variables defined between its curly brackets.
- It has access to the parent function's variables.
- It has access to the global variables.
Let’s have a look at an example
function greet(greeting) {
setTimeout(function waitASec() {
console.log(greeting);
}, 1000);
}
greet("Eh, What's up doc?");
//Eh, What's up doc?
All of us have used a timer, setTimeout
or setInterval
at some point, we passed a function that referenced some variables outside of its scope, but we might not have realized that we’ve used a closure.
In the above example at the time that the function waitASec
runs the function greet
is already over and the variable greeting
that it is closed over shouldn’t have existed but it did because closure preserves it.
The waitASec
function is closed over the variable greeting
. Coming back to lexical scope, the function scope of waitASec
is able to access the variable defined in the parent scope of function greet
because that is where it is defined. Hence it is able to use the variable even after the outer function is done executing.
Normally people would assume that a snapshot of the variable is being sent to the function but in reality, the function is keeping the variables in the outer function alive.
A Closure closes over a variable as it keeps in context the variable itself and not a snapshot of the variable or the value at a particular point in time.
Where do we use it?
We probably use closures many times a day without realizing it like in callbacks.
Callbacks
For example:
function getMarblesByColor(color) {
return marbles.filter(marble => marble.color === color);
}
color
is available in the callback because of lexical scoping, and the value of it is persisted when the callback function is called by filter
because of a closure.
Private Variables
A timer built using the concept of closures
const makeTimer = () => {
let elapsed = 0;
const increase = () => elapsed++;
const stopwatch = () => elapsed;
setInterval(increase, 1000);
return stopwatch;
};
let timer = makeTimer();
timer(); // Returns the elapsed time in seconds
let timer2 = makeTimer();
timer2()
In the above example, the function makeTimer
returns a function stopwatch
which has access to the variable elapsed
even after makeTimer
has finished executing. This is because the function stopwatch
is a closure and it has access to elapsed
which is in its scope due to the lexical scoping of javascript.
This allows us to create two timers, timer1
and timer2
, that have their own private variable elapsed
. This is how we can create private variables and functions using closures.
Conclusion
Scope refers to the part of the program where the variables and expressions can be referenced.
JavaScript allows us to nest scopes. The variables declared in outer scopes are accessible from all inner ones. Where you define your functions determines what data it has access to when you call it.
A closure is a function that remembers its outer variables and can access them. Closures may not be used as much since JavaScript incorporated classes in ES6, but they can still be used to write clean reusable code.
Thanks for reading this article, and please feel free to leave any comments you have. I’m open to learning from you. Cheers!