A Drive Through Javascript Closures
📅 2020-11-16
Closures are a fundamental concept to Javascript but can be difficult for beginners to understand. To alleviate this, we will define the basic concept and then apply this abstract concept to something familiar like a car.
Basic Concept
A closure is a combination of a function and the outer scope. The outer scope can refer to variables or other functions.
A simple example would be:
var outerVar = "value";
function enclosedFunc() {
console.log(outerVar);
}
enclosedFunc()
// value
Here enclosedFunc is the enclosed function that prints outerVar. The outer scope of enclosedFunc is the global scope, which includes outerVar. When executing enclosedFunc, it has access to outerVar and prints “value”. The example shows a closure in the simplest form. A function is a closure. Thus, every function is a closure. The function does not need to be nested to be a closure. However, this does not show how closures can be useful. The following analogy will demonstrate a potential use-case for closures.
Analogy
Closures can be used like private functions. When a closure is nested inside an outer function, it has access to the variables and functions of the outer function. This is true even after the outer function has finished executing.
Let’s use a car as an analogy:
A car closure sitting in the factory closure
Here is the car, represented in Javascript:
function makeCar() {
place = 'factory'
function car(destination) {
console.log(`Started at ${place}`)
place = destination
console.log(`Arrvied at ${place}`)
}
console.log(`Constructed at ${place}`)
return car
}
There are two closures here:
- makeCar
- car
The outer makeCar closure creates each instance of a car.
The inner car closure allows the car to drive to a destination. Since car is nested inside of makeCar, it has access to the place variable.
In our analogy, we will focus only on the car closure, as the function represents the body of the car. This would be things like the engine, tires, and doors.
The place variable represents the surrounding location of the car, not a component of the car itself. The ability to move allows the car to change its surrounding location. Thus, the place variable fits into the outer scope of the car closure.
Now we will make a car and drive to a destination.
carFL = makeCar()
// Constructed at factory
carFL('miami')
// Started at factory
// Arrvied at miami
The carFL variable is initialized to the car function with the place variable initially set to “factory”. Executing carFL updates the place variable from “factory” to “miami”.
When making two cars, each one is able to drive to their own destinations.
carFL = makeCar()
// Constructed at factory
carWA = makeCar()
// Constructed at factory
carFL('miami')
// Started at factory
// Arrvied at miami
carWA('seattle')
// Started at factory
// Arrvied at seattle
carFL('jacksonville')
// Started at miami
// Arrvied at jacksonville
Even though both carFL and carWA closures have a place variable, their scopes encapsulate their own version of it.
Both cars start in the “factory”. Then carFL drives to “miami” and “jacksonville”, while carWA drives to “seattle”. This shows that a car driving to one destination will not affect the other cars, even though they all originated from makeCar.
Also note that when executing carFL(‘jacksonville’), it starts at “miami” and was not reset to “factory”.
Conclusion
Like cars, closures are a useful tool in Javascript. There are many use cases for closures and understanding them is an important part of our journey to become better developers.
For further reading about closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures