Follow along from Part I
In my previous article, we have covered the concept of Lexical Scope, a building bloc to understand closure. We have seen that we can use a variable to keep track of the digits that make up the phone number. We also worned that at this stage, there is a serious flaw in our code. In this article, we will talk about the existing flaw, and how to fix it with nested function, the other building bloc to understand and use closure.
What’s the flaw?
At this stage we have the following code
1// intialise the number to an empty string2let dialledNumber = '';34function phonePad (digit) {5 // concatanate each new digit to build the dialled number6 dialledNumber = dialledNumber.concat(digit);7 console.log('The phone number to call is: ' + dialledNumber);8}910phonePad('0'); // The phone number to call is: 011phonePad('6'); // The phone number to call is: 0612phonePad('5'); // The phone number to call is: 065
It does the job, we are able to store each new digit in the “dialledNumber” variable. And because “dialledNumber” is in the Lexical scope of phonePad() we are all good …
However, “dialledNumber” is acting as a global variable here (accessible/mutable by any intermediat scripts). It is therefore “unprotected”. It means that “dialledNumber” can be accidently (or not) used by intermediat script for other puroposes. If that happen, our phone pad logic would be broken. The line 13 below will break the app
1// intialise the number to an empty string2let dialledNumber = '';34function phonePad (digit) {5 dialledNumber = dialledNumber.concat(digit);6 console.log('The phone number to call is: ' + dialledNumber);7}89phonePad('0'); // The phone number to call is: 010phonePad('6'); // The phone number to call is: 0611// next line will mess up our phone pad memory12dialledNumber = "Random assigment"13phonePad('5'); // The phone number to call is: Random assigment5
Protecting the pad memory
Therefore, we need to find a way to protected “dialledNumber” variable. We can do just that moving it inside phonePad(). This will make it accessible only from inside phonePad(), and take it off the global scope now. In other words, “dialledNumber” become a private variable to phonePad()
So lets try that here
1function phonePad (digit) {2 // intialise the number inside phonePad3 let dialledNumber = '';45 dialledNumber = dialledNumber.concat(digit);6 console.log('The phone number to call is: ' + dialledNumber);7}8phonePad('0'); // The phone number to call is: 09phonePad('6'); // The phone number to call is: 610phonePad('5'); // The phone number to call is: 7
But wait! what happened? the phonePad memory is gone !
Here is what is happening. Each time we call phonePad(), javascript creates a brand new instance of that function. The previous instance just went to what is called “garbage collector”, or just garbage if you want. Then, how do we get back that memory while still keeping “dialledNumber” private?
We can do that in two folds.
- Create a nested function
- Create a reference to the nested function.
Nested function & Reference
It name says it all. A nested function, is a function inside (nested) another function. So we will nest another function inside phonePad(). But that’s not enough, we also have to follow extra steps to make the phone pad working. Following are all the steps that we will follow :
- Nest an anonymous function inside phonePad().
- Remove “digit” as phonePad(
digit) argument, and set it as the anonymous function(digit) argument instead. - Return the anonymous function.
- Return the dialledNumber variable from the anonymous function scope
- Assign phonePad() function to a “pad” variable
- Use the variable “pad” to serve as reference and to pass the selected digits
The code should look like this
1function phonePad () {2 // intialise the number string3 let dialledNumber = "Phone number is: ";4 // declare the nested function receiving digits as argument5 // And return it6 return function (digit){7 // concatanate each new digit to build the phone number8 dialledNumber = dialledNumber.concat(digit);9 // return the concatanated digits10 return dialledNumber11 }12}13// assign phonePad() to a variable that14// will serve as reference/link15const pad = phonePad()16// send each new nmuber to pad()17// and display the phone number18console.log(pad(0))19console.log(pad(6))20console.log(pad(5))21console.log(pad(7))
And the output
Voila! Remember, by default functions in javascripts can not hold sate (information) beyond its instance life cycle. What we did here is to enable just that by:
- Protecting “dialledNumber”, making it a “private” variable that can hold state.
- Nesting a function that take care of updating of the state (protected variable “dialledNumber”)
- Keeping phonePad() instance “alive” by using a reference to its nested function.
The 3 above steps allow us to create a function that is able to hold a private state (the phone number). Which would not be possible whithout closure. This is what Closure is all about :)
You probably still wonder how Closures are being used concretly to make things happen in “real” app …
That will be the subject of the last article for this serie for a perfect Closure ;)