YDKJS — Scopes and Closures — Part5
Let consider a simple example without closure, which we will refactor to use closure later.
The above is a simple example of an inner() inside an outer(). The inner() is both declared and executed from inside the outer function.
Now, since in JS functions are first class citizen. We can have the function inner executed from anywhere else also. We will refactor the above to do so using function expressions.
So, we changed inner to a function expression and returned it. So, when we call outer, we can save the return value to a variable innerFn.
Let’s understand one thing. The variable b have an scope in outer function ie from line 4 to line 10. So, at line 13 when we call the outer function, it can access the value of b, but at line 14 there is no outer function.
So, how does the innerFn() access the value of b. This is where the JS feature of Closures comes into play. When the var inner is created at line 6, the JS engine not only stores the function object information but also it’s scope information. So, it stores a scope of variable a and b inside the inner function object.
Now it doesn’t matter where you call inner, whether in this file or some third party file. It will always remember the value of a and b, as if a snapshot is been taken.
It is to be noticed that the snapshot is a pointer to these variables and not a copy. So, if you change variable a and b here, it will be changed in function inner.
One thing to also know is if we call outer 2 times then how many copies of a and b gets created.
As seen above to added line in inner to increment a and b variables. Now since a is global variable it’s value is 11 first time and 12 the second time then innerFn2 is called.
But b have an snapshot in the inner function, so each new call through a new innerFn will start b at 20 and print it as 21 because of the increment.
Here the setTimeout at line 7, is calling a function fn. This fn is a function expression console log the variable a. Now when this fn is created it gets the snapshot of var a and even if the setTimeout is called from another file which have no concept of var a, this variable gets prints after a delay of 6 seconds.
Now, let’s consider another practical use of closures. We have the concept of getters and setter in OO languages like Java. By this we hide the variables from outside world in a function/class and make them private. They can only be access via getter methods and set via setter methods.
Here we created a person object and have two properties of firstName and lastName holding the value. And then two getters getFirstName and getLastname to get those values.
As we can see in the console, we are able to do a person.getFirstName() but we are also able to do a person.firstName
So, we will refactor our code to use the concept of closures to achieve the same. Consider the below code.
We first put the object in a function createPerson. Then we move the var firstName and lastName outside the object. We have the usual getFirstName and getLastname inside the object. We then return the object.
Now at line 16, we call the function and then assign the return value ie the object in a variable person.
The person variable can access the getFirstName but not the firstName. It is because firstName scope is from line 2 to line 13 and not accessible outside it.
But the person.getFirstName()is a closure executed. When the getFirstName was created at line 6, it got the snapshot of the variable firstName because of the concept of closure. This is how person.getFirstName( )is able to access the value of firstName.
This concludes our You Don’t know JS — Scopes and Closures series. You can find the Object and Prototype series here.