ABC - Always Be Closing
According to the Wikipedia, a closure “is a technique for implementing lexically scoped name binding in a language with first-class functions.” Do you remember the latest blog post? Exactly. WAT🐤.
When I read this, I have to read it again. Okay, maybe one more time. Then try to remember what it is meant by first-class functions in programming terms. I have a guess but I am not completely sure. So I go to the Wikipedia again to check the definition of first-class functions and it confuses me more. Then I go check some examples of closures and remember what closure was and why it is so cool. To make you avoid this hassle, here is an explanation like you are five years old.
The following is obviously not true at all times. However, when you hear the word closure, think about a function inside of another function. The inner function exploits the lexical scope of the outer function. In other words, the inner function remembers the variables from the outer functions’s lexical scope.
Let us take a look below to understand closure by an example. The example can be familiar to you if you ever used any frontend framework or you are not living under a rock. The React and Vue starter templates come with a counter that you can increment using a button. Of course, in their example they use their own state APIs. Below, we will imitate the behaviour using a closure. If you are using Angular, why?
Let’s start with how not to write a counter function.
let count = 0;const counter = () => { count++ return count;}console.log(counter()) // 1console.log(counter()) // 2console.log(counter()) // 3
let count = 0;const counter = () => { count++ return count;}console.log(counter()) // 1console.log(counter()) // 2console.log(counter()) // 3
This is all good until we want another counter component. As soon as we want to have a second instance of our counter, we will have state problems because both of the instances will use the same “global” count variable. To avoid this, we use a closure.
const makeCounter = () => { let count = 0; return () => { count++ return count; }}const counter1 = makeCounter()const counter2 = makeCounter()console.log(counter1()) // 1console.log(counter1()) // 2console.log(counter1()) // 3console.log(counter2()) // 1console.log(counter2()) // 2
const makeCounter = () => { let count = 0; return () => { count++ return count; }}const counter1 = makeCounter()const counter2 = makeCounter()console.log(counter1()) // 1console.log(counter1()) // 2console.log(counter1()) // 3console.log(counter2()) // 1console.log(counter2()) // 2
As you can see, by using a closure, we give each counter its own internal count state. Each counter instance increments its own count value and does not interfere with the internal states of other counters.
Using closures, currying can also be done. A function can return another function that uses one of the input parameters as a static variable and the other input parameter as variable.
const multiply = (multiplier, input) => { return (input) => { return multiplier * input }}const double = multiply(2)const triple = multiply(3)console.log(double(5)) // 10console.log(triple(5)) // 15
const multiply = (multiplier, input) => { return (input) => { return multiplier * input }}const double = multiply(2)const triple = multiply(3)console.log(double(5)) // 10console.log(triple(5)) // 15
Now you know. Use them. Do it.