Hi, I hope you are doing well, A lot of times I have encountered people who consider JS as some sort of' bandage fix' language, some say it is a duct tape of the internet, the people that say these words don't appreciate the beauty of this language, but I do and would always want that you should also do.
But why do people call this beautiful language with all these names, there are multiple reasons, and one of the major reasons is the this
keyword.
Before starting onto the this
keyword I would like to tell you a small thing that will be handy throughout this blog, so there is a thing called global object, which differs from one place to another according to the environment in which JS is running, also sometimes depends on the mode in which JS is running i.e strict mode
. If you want I can write a separate blog regarding all of these, but for the time being, all the examples that I will be showing in this blog are -
- Executed on the browser.
strict mode
is not enabled.
which in turn means, that the global object is the window
object, which I might sometimes refer to as a bad this
, please bear with me, so without further ado let's dive right into it.
I will tell you the basic reason why this
becomes a tough thing to understand because it changes from one place to another, it is contextual, it changes with the involvement of others.
Let's start with the easy things first. Try executing the following code in the browser (console).
console.log(this)
if(true){
console.log(this)
}
The above code will print the global object in this case, the widow
object twice. Things to learn from this-- this
doesn't change outside a function and this
doesn't depend on any block of code.
Now let's get the party started, try to guess the output of the following piece of code.
function walk() {
return `${this.fName} started walking`
}
const me = {
fName: "Mihir",
walk,
}
console.log(me.walk())
console.log(walk())
Strange right -
me.walk()
logs -- Mihir started walkingwalk()
logs -- undefined started walking
This particular thing happens because in the first case the walk()
is associated with an object me
which has a property as fName
so when we call me.walk()
, the this
inside the walk()
is resolved as the object me
, in other words, the this
points to the object me
inside walk()
when invoked using me.walk()
.
Although this is not the use case when you invoke the function directly. In that case, the this
inside the walk()
is the window
object, and that doesn't have any property called fName
, that is why this.fName
is resolved as undefined
.
To prove the above-mentioned point, we will console.log(this)
inside the walk()
function.
If you have an object that you can't alter, you can still bind our good old walk()
with that object and use it. You can do this with the methods -- call
,bind
,apply
. These functions are present on the Function.prototype
, If you want I can write a blog post of how these functions work internally, but for the time being, we will just use it to our power.
function walk(){
return `${this.fName} started walking`
}
const FixedMe={
fName: "Mihir"
}
const meWalk=walk.bind(FixedMe)
meWalk()
Now, why does this work? I will help you, so the bind()
returns a new function with the this
in the function pointing to the object which you passed as an argument to the bind()
.
Now let's see how this
works inside a constructor function --
function Person(n,a) {
// Properties
this.name=n;
this.age=a;
}
// Methods
Person.prototype.walk=function() {
return `${this.name} started walking`
}
const me=new Person("Mihir",21)
me.walk();
Now, let's understand how this
works over here, but before understanding that you need to understand what new
does over here, this keyword new
is such a powerhouse, basically, it follows a 4 step process, but let me tell you the mental model for it. It basically creates a new object binds it with the this
inside the Function and returns that object. To prove this point I can do console.log(JSON.stringify(this))
, and you will see a {}
. If you want I can write a blog regarding the new
keyword, in that I can deep dive into the 4 step process of what the new
keyword does.
Here I have used JSON.stringify(this)
as this object is later assigned to me
, inside that the name and age property is also updated, as it is a reference, inside the dev tools you will also see the name
,age
property if I just console.log(this)
, to know exactly what was this
at the particular moment, I changed the object to a string, which shows the actual representation at that particular moment.
Now let's talk about callback functions, let me dive into an example --
function Person(n) {
this.fName=n
setTimeout(function() {
console.log("Inside cb()");
console.log(this.fName);
}, 100)
}
const me=new Person('Mihir')
Guessed the output, or you are pulling your hair right now? Let me explain so what happened over here is, the callback function actually doesn't run in the same context as the Person()
, it moves out of the main thread and actually executed later. I know it went a little wild out here, moving out of the main thread, event loop, I would be very happy to write about all of this too, but currently not in the scope of this particular blog. For the time being, just think that the callback function is not executed in the Person()
context. So the this
becomes the global object i.e window
and there is no property fName
on that, so it shows undefined
. So how to solve this problem? Two ways --
setTimeout(function() {
console.log("Inside cb()");
console.log(this.fName);
}.bind(this), 100)
Here we used the bind()
and associate this particular function with the this
, and this -- this
is actually present in the context of Person()
after the callback function is completely declared, the context of Person()
starts, and that my friend is the big game happening.
The area marked in red is the callback function that is not in the scope of Person()
Another way to fix it? Here we go --
setTimeout(()=> {
console.log("Inside cb()");
console.log(this.fName);
}, 100)
Here we used the new ES6 arrow function, and it works why? Because over here the this
is actually the same this
present inside the context of Person()
.
The arrow function creates an auto-binding of
this
, with thethis
that is present inside the surrounding scope, at the time of object creation.
I know it is a lot to take, and the arrow function example probably blew your mind. You can check out this blog -- "when not to use arrow functions"
If you made it through to this, congrats! I will make things easier for you after this, so how do you resolve this
? let us summarize
Summary
- If the
new
keyword is used when calling the function,this
inside the function is a brand new object.function ConstructorFn() { console.log(JSON.strigify(this)); // {} this.value = 10; console.log(this); // { value: 10} } new ConstructorFn();
- If
apply
,call
, orbind
are used to call a function,this
inside the function is the object that is passed in as the argument to the function calls above.function fn() { console.log(this); } const obj = { value: 5 }; const boundFn = fn.bind(obj); boundFn(); // { value: 5 } fn.call(obj); // { value: 5 } fn.apply(obj); // { value: 5 }
- If a function is called as a method — that is, if dot notation is used to invoke the function —
this
is the object that is used to call the method (left side of the dot).const obj = { value: 5, printThis: function() { console.log(this); } }; obj.printThis(); // -> { value: 5, printThis: ƒ }
- If a function is invoked as a free function invocation, meaning it was invoked without any of the conditions presented above,
this
is the global object, i.ewindow
.function fn() { console.log(this); } // If called in browser: fn(); // Window {stop: ƒ, open: ƒ, alert: ƒ, ...}
If multiple of the above rules applies, the rule that is higher wins and will set the
this
value.const obj1 = { value: 'hi', print: function() { console.log(this); }, }; const obj2 = { value: 17 };
If rules 2 and 3 both apply, rule 2 takes precedence -->
obj1.print.call(obj2); // { value: 17 }
If rules 1 and 3 both apply, rule 1 takes precedence -->
new obj1.print(); // {}
If the function is an ES6 arrow function, it ignores all the rules above and receives the
this
value of its surrounding scope at the time it’s created.
Woohoo! Now you have become a this
god. Thank you for bearing with me for so long. Thank you and have a nice day!
Reference - Set of rules for this