If you are like me, you have spent several hours trying to understand this. You have heard terms like, a function binds its own this, and you didn't get it. I have been there, that is why I am writing this article. The goal is to help you understand the concept in simple, clear terms.
This article is targeted at the following groups:
• junior developers
• senior developers
The following are the prerequisite knowledge required:
• Functions in JavaScript
• An understanding of the window object
• Class syntax in JavaScript
• Objects in JavaScript
• Event listeners in JavaScript
This article does not cover advanced edge cases of the this keyword, please read the docs here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this.
The article is in two parts:
Part 1, discusses the everyday use of this:
• what is this
• this in regular functions
Part 2 covers the following:
• this in arrow functions
• this in special cases
What is this
? this
is a special keyword in JavaScript. It always refers to an object, without exception. So, we have solved the first part, this is a pointer in JavaScript. Onward, we go. The second part, this is always declared in a function. This is key to understanding this
. For clarity, here are the rules again:
• this is always a pointer to an object.
• this is always defined inside a function.
Let's see a quick example:
let obj = {
country : 'nigeria',
getCountry(){
return this.country;
}
};
Don't worry about the code, it will be explained, just observe that this
is in a function, and it refers to the object- obj
.
Regular functions(for simplicity, will refer to any function other than arrow functions) and this
. Read closely here. When this
is defined in a regular function, it points to the object that invokes the function. In other words, it points to the object which called the function. This means that this
in a regular function is NOT sure what to point to until the function is invoked. Consider the simple example above:
• A function inside an object like this is called a method.
• this
in the getCountry
function doesn't know what object to point to just yet, it's as confused as you and I.
• the value of this
becomes clear to JavaScript, when you invoke(run or call) the function.
• the value of this
, is set to the object that directly calls the function.
• So, to call the getCountry
method, we write:
obj.getCountry();
• Guess what this refers to.
• Yes, it refers to the obj
object, as it was the object that called the function getCountry.
• Hence, the output will be: nigeria.
Let's consider something more interesting. Given the following expressions, determine the output, and explain why. Try this yourself, before seeing my explanation.
First example:
function logger(str){
return this.str;
}
logger(‘John’) // ??
Second example:
let obj = {
name : 'John',
getName(){
function anotherFunc(){
return this.name;
}
return anotherFunc();
}
}
obj.getName() // ??
How did that go? It doesn't matter, so long you tried. Now, let's consider the first example:
• When you call the function, logger
, what happens?
• All JavaScript functions run within an object behind the scenes. This object is referred to as the context of the function.
• A little trick, to determine the context of a function is to look to the left of the function when it is invoked.
• this
always refers to the context.
• To call the logger
function, we write logger();
• In reality, this is is what is happening : window.logger();
• This is because the window
object is the closest object to the function, hence it is its context.
• The function logs undefined
, as the str
property doesn't exist on the window object.
• If we remove the str
parameter from the function, and just return this
, you get the window
object.
In this second example, to access the getName
method, we write obj.getName()
,but we get undefined
as our result. This happens because our method returns another function. A function nested inside of the getName
method- a nested function. The nested function is the one with this
.
What does this
point to? Well, let's try to call the nested function and then look left.
obj.getName.anotherFunc()
As you can see, the closest caller of the anotherFunc
function is not an object but a method: getName
. But this
doesn't point to a function, ever. What is really happening?
Well, this is it:
obj.getName.window.anotherfunc
Hence the object closest to anotherFunc
is the window.
This object doesn't have a name property on it, so it returns undefined
.
This logic is true for all regular functions nested in a method, no matter how deep the nesting, the context is always the window object. You can try this out yourself.
We have discussed some key concepts thus far, I advise you to practice some questions at this point to test your understanding.
Try these two(answers are immediately after both questions):
Question 1:
const object = {
message: 'Hello, World!',
getMessage() {
const message = 'Hello, Earth!';
return this.message;
}
};
console.log(object.getMessage()); // What is logged?
Question 2:
const object = {
message: 'Hello, World!'
};
function logMessage() {
console.log(this.message); //
}
logMessage(); // ??
These questions are gotten from Dmitri Pavlutin, you can check out his blog here for more : https://dmitripavlutin.com/javascript-this-interview-questions/
Answer to question 1:
Hello, World!
Answer to question 2:
undefined
The concepts that we have discussed so far are the natural ways this works. This is what is called implicit binding of this. But sometimes, we want to force this
to behave in a more flexible manner. For example, in the anotherFunc
above, say we want this
to point obj
, rather than the window
object. Well, we must explicitly tell JavaScript to do that.
Explicit this binding can be achieved in one of three simple ways:
• call
(context, arg)
• apply
(context, [arg])
• bind
(context, arg)
The call method is applied to a function to change the context of the function i.e to change what this
is points to. We can change it to whatever we like.
To change anotherFunc
to reference our obj
object, we reframe our object like this:
let obj = {
name : 'John',
getName(){
function anotherFunc(){
return this.name;
}
return anotherFunc.call(obj);
}
}
obj.getName() // "John"
The second parameter of the call method is args
, which refers to the argument you want to pass into the function. Here is an example:
function welcome(event){
return 'Hello ' + this.name + ' welcome to the ' + event
}
let obj = {
name : 'John'
}
welcome.call(obj,'Oscars'); //
'Hello John welcome to the Oscars'
The apply method works exactly like the call method ,except it takes args
in the form of an array. For example:
function welcome(a, b, c){
console.log('Hello ' + this.name + ' welcome to the ' + a);
console.log('Hello ' + this.name + ' welcome to the ' + b);
console.log('Hello ' + this.name + ' welcome to the ' + c);
}
let obj = {
name : 'John'
}
let events = ['Grammy', 'Oscars', 'World cup'];
welcome.apply(obj, events);
// Hello John welcome to the Grammy
// Hello John welcome to the Oscars
// Hello John welcome to the World cup
//a, b, and c ---> the indices of the events elements.
The bind method works like the call method, however it returns a new function that can be called later. For example:
function welcome(event){
return 'Hello ' + this.name + ' welcome to the ' + event
}
let obj = {
name : 'John'
}
let bindFunc = welcome.bind(obj,'Oscars');
bindFunc(); //
'Hello John welcome to the Oscars'
I hope this article, has clarified this for you. Part 2 will go into some quirky parts of this
, for now, cheers and see you soon.
Top comments (0)