Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Your knowledge of JavaScript can be pretty well judged by your understanding of this. The fact that people find it difficult to comprehend is, because of its use in multiple and varied scenarios.
Lets get them right once and for all.
If I were to talk about this in literal sense, it simply meansâââthis very thing in front of me. âThis Jacket is Coolâ, claimed my sister holding that jacket in her hand. I replied back saying, âNo, this oneâs better!â I was referring to the jacket near to me and she was referring to the one near her and hence the use of this in respective statements.
Value of this depends on1. The Execution Context2. Invocation
Execution Context
Execution Context is the environment in which the code runs. For example, the local variables of a function would be accessible in that function only and not outside of it. This is called as Functional Level Scoping or Local Execution Context. Variables declared in global scope (outside of any function) would be accessible in all the underlying functions. This is called as Global Execution Context.
Letâs consider this example to understand the difference between the two of them -
The value of this in Global Execution Context by default is window object.
The value of this in any Local Context depends on the way the function is called.
I. Calling a function in global context would have its this value as window
II. Calling a function that runs JavaScript statements in strict mode would have this as undefined
III. Calling a function that is inside an object
Function add is one of the properties on object obj. When we call add with reference to obj as obj.add(), it gets called in the context of obj. Properties a & b have definite value in the context of obj, so that result of obj.add() is defined.
cacheAdd variable stores the reference of add function. If we execute this function at a later stage, it will run in the context from where it is called. In our example, weâre calling cacheAdd in the global context, so it will have global object as the this reference. Since a & b are not defined in the global context, weâre getting the value of a+b as NaN.
To prove this point, lets define a & b in the global context.
obj.add() evaluates to 3 as the value of a & b defined in obj is 1 & 2 respectively. When we run add after caching it in a variable and then executing it in global context, it evaluates to 7 as the value of a & b in global context is 3 & 4 respectively. So, a function that uses this may give different result depending on the context in which it is executed.
Lets try another interesting example â
In this case, add function returns another function and the value of a+b is executed in this inner function. obj.add() will return a function that has one console statement. So, weâve to execute this returned function in order to get the sum of a & b. But this returned function (returned on executing obj.add()) will run in global context and hence the value of a+b evaluates to 7 (Value of a & b in global context is 3 & 4 respectively).
Here, add function prints the value of a & b after a timeout of 500ms. setTimeout runs in the global context.
Why setTimeout runs in the global context? (Iâll explain about it in detail in my next post.)
this takes the reference of the immediate object calling the function
Here, parentObj has childObj as one of the properties and add function is defined on childObj. When we call add as parentObj.childObj.add(), add function takes the this reference of childObj and variables a & b are not accessible from the local context of childObj. Hence the value of a & b is undefined.
Linking to the prototype chain
If we instantiate parentObj to create childObj, its properties would be accessible in the childObj context.
Using Object.create
When add function gets called in the context of childObj, it first tries to look for values of a, b & c in the context of childObj and if any of these values is undefined, it looks up to its parent context, which in this case is parentObj.
Using new operator
In this case also, properties of parent are available in the context of child. As we can see in the trailing console statement, the constructor of child is defined as Parent. So, it first checks for values in the context it is called and then look up to its prototype chain.
This ainât Rocket Science!
By default, the code runs in global context i.e. it has access to the variables defined in the context of window object. In each of the above cases, weâre just restricting the context to obj object. All the operations related to this would be carried out in the exact same way as they would have if the context was window. Even with global context, if any of the values is not defined, it looks up to the prototype chain.
As we can see, toString function is defined in __proto__ property of obj, which means it is defined on prototype of Object (Because obj is created by applying new operator to Object). As toString is not defined in the context of obj, it looks up to its prototype chain.
Letâs solve an interesting issue here â
My sister stubbornly refuses to share the candy with me and to support her claim, she says, she is the owner of this candy.
This is her Candy â
So, somehow Iâve to modify the this reference that her function whosCandyIsThis is using. Little toddler doesnât know that JavaScript is an unconventional language. You can always mend it the way you want.
Call, Apply & BindâââHere IÂ go!
With these 3 beautiful functions, you can modify the value of this reference. In the above example, candy.whosCandyIsThis.call(myCandy) modifies the this reference of whosCandyIsThis function to myCandy.
What is call all about?call explicitly pass the this reference to a function. In this case, it tells whosCandyisThis function to use this as myCandy.First argument of call is the this reference and if we have to pass in any additional parameters, you can do something like this candy.whosCandyisThis.call(myCandy, true)
Letâs take one more example
Here, weâre passing this reference explicitly to the displayMenu function defined in the hotel object to display menu accordingly. (Isnât this cool?)
But what if I want to display the menu based on userâs location. For that, weâll pass an additional parameter location to displayMenu function. Letâs modify the above code to fit in our requirements.
The first parameter of call is the this reference and then you can pass in any number of arguments that you want. displayMenu function now very well shows the list of menu items as per the location parameter. (Sounds good?)
Apply operates in the same way as call, the only difference being the way in which we pass arguments. Apply takes in the first parameter as the this reference same as that of call but other parameters are to be passed as an array.
Another function that can modify the this reference is bind. Unlike call and apply that returns the result of the function, bind returns a function. And this returned function can then be called at any later stage. It will run in the context of explicitly passed bounded argument.
hotel.displayMenu.bind(ccd) returns a function that has this reference of ccd. When we run boundFunction, it runs in the context of ccd and hence prints Espresso for location A.
I was trying to experiment my instincts. If I bind an already bounded function to some other reference, would that function run in the context of the later binding? If this is true, I can do this endlessly and keep changing this reference with all the power I have! This is not true. You can only bind a function once. In the above example, Iâve tried to bind the bounded function again with pizzaCentre but when I execute this function, it prints the previous result which means its reference still points to the first binding (ccd).
Well again, JavaScript is an unconventional language. If thereâs freedom to modify anything as you wish, there are guards who protect the integrity. One such masterpiece is our cute Arrow Function.
This is how it works. Plain &Â Easy.
Why are we even talking about arrow functions in this article? Whatâs so special about them?
In arrow function, the value of this is that of its enclosing lexical contextLexical Context is block level scoping. It does not change with call, apply or bind.
Arrow functions maintain the binding of this of its enclosing lexical context.
Crape! This Fat Arrow took away my candy.
As you can see, whosCandyIsThis is defined as an arrow function. So it will maintain the binding of its Lexical Context. On instantiating Candy, niks sets the owner and flavor for her candy. whosCandyIsThis function gets bounded to niks reference.
So, When I try to call it explicitly by passing this reference using call, apply or bind, it doesnât work. The bindings of niks.whosCandyIsThis function cannot be changed, whatsoever!
You might like my previous articles on Service Workers â
Service Workers Fundamentalsâââhttps://hackernoon.com/service-workers-62a7b14aa63a
Building Pokemon App to evaluate the power of Berries & Service Workerâââhttps://hackernoon.com/building-pokemon-app-to-evaluate-the-power-of-berries-service-worker-176d7c4e70e3
Lets get this `this` once and for all was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.