Understanding ‘this’ keyword in javascript
one of the important concepts that most javascript developers are scared of.
What is this keyword?
Definition : this is a reference to the object to which the function is a property of.
Woah!, what did I just say???? Let’s look at the statement one piece at a time.
i) this is a reference to an object.
ii)to which the function is a property of.
Ex 1:
const obj = {
name : “samanth”,
welcome() {
console.log(`Welcome ${this.name}`)
}
}
obj.welcome(); // Welcome samanth
Here we are using this inside the welcome function, which is a property of object obj. In this context this refers to object obj.
Wait! what if we use this keyword in an empty javascript file instead of function body?
Even though we write our script in an empty JS file, it will be run inside a special function called anonymous.
Inside this anonymous function this keyword refers to window object. because this function is a property of window object.
So far so good, but why this keyword is considered as a complex concept to understand?
Because this is dynamically scoped in javascript where as, rest of the things are lexically scoped in javascript.
Dynamically Scoped : Based on the context in which we use this keyword in, the object to which this points will differ. If we observe in the first example this pointed to obj object, in the second example this pointed to window object.
Let’s look at an example that can hurt your brain :-)
Ex 3:
const obj = {
name : “samanth”,
welcome() {
const getWelcomeMsg = function() {
return `Welcome ${this.name}`;
}
console.log(getWelcomeMsg());
}
}
obj.welcome(); // error: Cannot read property ‘name’ of undefined
This example is an extension of example 1, where instead of directly accessing this in welcome function, we created and inner function getWelcomeMsg which will use this and returns a welcome string.
Explanation :
Here the funtion invoking is similar to obj.welcome(getWelcomeMsg()), getWelcomeMsg is not explicitly getting called through object obj.
Note : **Whenever a function is not called explicitly through a custom object, it will be defaulted to window object.
Interesting example to clearly understand the dynamic nature of this keyword.
Let’s modify our easy to understand example 1 once again.
Ex 4 :
const obj = {
name : "samanth",
welcome() {
console.log(`Welcome ${this.name}`);
}
}const newWelcome = obj.welcome;obj.welcome(); // Welcome samanth
newWelcome(); // error: Cannot read property ‘name’ of undefined
Explanation :
Here we are copying welcome method which is using this keyword and calling it in two ways (observe the output change based on how this functions is called).
- We are directly calling welcome function using obj.welcome() (here this refers to obj and since obj has name property it will log Welcome samanth)
- In this case we are assigning welcome function to a property called
newWelcome which is defined globally . In this case this points to window object (look at example 3 note) and since name is not defined on window object we will get an error.
Note : If you want learn about lexically scoping vs dynamically scoping refer to this article.
So how can we fix Ex 3 to make this lexically scoped?
We have multiple solutions for this :
- using ES6 arrow functions.
- using JavaScript bind method.
- copying this into a local variable.
Method 1:
Arrow functions introduced in ES6 makes this lexically scoped (instead of giving access to variables and methods based on where the function is called , access will be given based on where the function is written).
EX 3 fix :
const obj = {
name : “samanth”,
welcome() {
const getWelcomeMsg = () => {
return `Welcome ${this.name}`;
}
console.log(getWelcomeMsg());
}
}
obj.welcome(); // Welcome samanth
If you observe we just modified getWelcomeMsg to an arrow function instead of a normal function and this became lexically scoped.
Method 2 :
We can use JavaScript bind to bind this keyword to the given function.
const obj = {
name : “samanth”,
welcome() {
const getWelcomeMsg = function() {
return `Welcome ${this.name}`;
}
console.log(getWelcomeMsg.bind(this)());
}
}
obj.welcome(); // Welcome samanth
we are binding outer this context(context of obj object) to getWelcomeMsg and calling that method.
Method 3 :
We can cache this of the required context and pass that to the function we want.
const obj = {
name : “samanth”,
welcome() {
const self = this;
const getWelcomeMsg = function() {
return `Welcome ${self.name}`;
}
console.log(getWelcomeMsg());
}
}
obj.welcome(); // Welcome samanth
Look at how we cached this using a variable named self and accessed name property of obj.
Final Note :
Hope you enjoyed and got some useful information from this article. If you feel like you didn’t understand some parts of this article, please go through this article again and i’m sure you’ll definitely understand. If you still feel something is missing, let me know at sai12999.k@gmail.com or in the comments.