JavaScript "this" keyword thoroughly explained!
The "this" keyword is one of the most confusing topics. Let's look at it in detail.
Introduction
"this" keyword is a little different in JavaScript when compared to some other languages. It is dynamic and flexible. And almost everyone gets confused by it in the beginning. So let's look at it in detail.
A function's "this" reference is determined entirely by how the function is called. The function's body does not even matter. Now, there are 4 ways you can invoke a function in JavaScript.
Ways to invoke a function
- object.function()
- call(), apply() and bind()
- new keyword
- simple parenthesis
As it depends on how the function is invoked, we have to see how "this" value is determined with each way.
obejct.function()
const workshop = {
teacher: 'Kashish',
ask(question) {
console.log(this.teacher, question);
},
};
workshop.ask("What will 'this' reference here?");
// Kashish What will 'this' reference here?}
Here "this" keyword will reference the object on which the function/ method is called. This is the easiest to understand and is similar to other languages. This is called implicit binding.
call() , apply() and bind()
Here all 3 functions will do a similar job. These functions take the first parameter as "this" reference where you can give any object. This way can be helpful when you want to explicitly give a reference to "this" value. Hence, it is called explicit binding.
function ask(question) {
console.log(this.teacher, question);
}
const workshop = {
teacher: 'Kashish',
};
const seminar = {
teacher: 'Oliver',
};
ask.bind(seminar)("What will 'this' reference here?");
// Oliver What will 'this' reference here?
This also explains why "this" keyword can be so useful. You can different objects and you can use the same function instead of writing one for each object.
"new" keyword
The "new" keyword is used when calling a function and it creates an empty object from nowhere and references it with the "this" keyword. Note: It overrides "this" keyword reference and sets it to the new object. This can be quite useful while using it in a class.
function ask(question) {
console.log(this.teacher, question);
}
const workshop = {
teacher: 'Kashish',
};
const newEmptyObject = new ask("What will 'this' reference here?");
// undefined What will 'this' reference here?
In the above code snippet, we get undefined as the value of "this.teacher" from ask function. Why? As "new" keyword created a new object and referenced "this" keyword value to that new object. The new object is empty and does not have a teacher property. And that is why we get undefined.
Simple parenthesis
Now comes the last way to invoke a function, the simple parenthesis to call a function. In this way, "this" is set to a default value. Hence, it is called default binding. Strict mode and non-strict mode have different default values, so it's important to take care of that. Non-strict mode "this" keyword value defaults to the global object. And Strict-mode "this" keyword value defaults to undefined.
const teacher = 'Kashish';
function ask(question) {
console.log(this.teacher, question);
}
function askAgain(question) {
'user strict';
console.log(this.teacher, question);
}
ask("What's the non-strict-mode default?")
// Kashish What's the non-strict-mode default?
askAgain("What's the strict-mode default?")
// TypeError
We got the teacher from the global scope as "this" in non-strict-mode defaults to global. And we got a TypeError because strict-mode "this" defaults to undefined and using undefined.teacher gives TypeError.
(The TypeError object represents an error when an operation could not be performed, typically but not exclusively when a value is not of the expected type.)
"this" in arrow functions
Now you may be wondering what happens in arrow functions. As contrary to what you might have heard, "this" in arrow functions does not point to the parent function. Actually, according to the official specs, arrow functions do not have the "this" keyword at all.
When you use the "this" keyword in the arrow function, Js treats it as a "this" variable and it does not exist in the function scope, it goes up to the outer scope to search for the "this" keyword value. So if you have 5 nested arrow functions, the Js will look for 5 functions up and get the value of the "this" keyword.
This may bring the parent "this" keyword value in the arrow function sometimes, but it is important to understand that it's not because of any special rule but just because of the lexical scope of JavaScript.
const workshop = {
teacher: 'Kashish',
ask(question) {
setTimeout(() => {
console.log(this.teacher, quesiton);
},1000);
},
};
workshop.ask("What is 'this' in arrow functions?");
// Kashish What is 'this' in arrow functions?
Here "this" keyword in the arrow function is not defined. So it will look up to one level up and get to ask function scope. Notice how we are running the function ask here. object.function() , so ask function will have workshop object as its "this" value so that value will be used by the arrow function's "this". That is why arrow functions are known to have "Lexical this".
How to determine
"this" value depends on the way the function is called. If you see a function referencing "this" keyword, just ask these four questions and you will be able to determine the value very easily.
- Is the function called by the "new" keyword?
- Is the function called by call() or apply() or bind() ?
- Is the function called on object context? ( object.function() )
- Is the function called by simple parenthesis? And is it in strict or non-strict mode?
Asking these questions in the exact order will give you the answer to the value of "this".
That's it!
I hope this explanation made your understanding better and you will be able to use "this" without any confusion from now on. If you have any further questions, you can reach me at kashishdhingra64@gmail.com or DM me on Twitter @kashish_web_dev Feedbacks will be highly appreciated!!
See you around!!