What is `this`? π΅
In English when we say this we are always referring to an object for example - this is a chair, here in this example chair is an object. Similarly in JavaScript, this
refers to an object. If we have to define what is this we can say that this
refers to an object it belongs to. But the value of this
is dynamic and is determined by in which context or how the function is invoked during the runtime. If you didn't understand a single thing don't worry just hang on because in this article all your doubts related to this
will cleared.
What exactly is this
and what is its value?
this
is a reserved keyword in JavaScript that always refers to an object. We have discussed above that the value of this
is dynamic which means that it changes. Let's understand this more clearly, when we are in our global context the value of this
is always going to be the global object which is the window object (if you're running your JavaScript code in the node environment then the global would be different). If we have a method (function inside an object) then this
of that method will point to the object in which it is defined. Let's write some code and see the value of this
.
this
in the global context
console.log("value of this is", this);
I assume you already know that this
will refer to the window object because we're in the global context. One more thing to remember is that whenever a function is invoked standalone this
of that function will always refer to the global object.
Output -
ππ» this
inside a function
We know that the value of this
also depends on how the function is called. Let's see this with an example.
function showFruits () {
console.log("this is" , this);
}
const garden = {
fruits : ['Mango', 'Banana', 'Apple'];
}
showFruits.call(garden); // This is an example of explicit binding (we'll learn about explicit binding in some other article)
garden.showThis = showFruits; // Here we are storing a property showThis and setting its value equal to the showFruits function
garden.showThis();
In the above code snippet we have used explicit binding as well as storing the showFruits
inside the garden
object. The call function that we have used here invokes a function for a specific object so that the function's this
will refer to the passed object. Don't worry we will cover explicit binding in full depth in our upcoming article.
Storing a method inside our object will bind the method's this
to that object. But that is only true if it's a function declaration.
If we have called the showFruits
function standalone like this showFruits()
then we'll this
will refer to the window object because the function showFruits
is not associated with any object or it doesn't have any explicit binding too.
Output -
ππ» this
inside a method
1) Function declaration
const user = {
username : "John Doe",
age : 19,
hobbies : ['Coding', 'Blogging', 'Designing'],
userInfo : function() {
console.log("this is" , this);
}
}
user.userInfo();
Here, we have a function inside our user
object that is created using function declaration and we're invoking it as a method and we already know that the value of this
is determined by how we invoke the function. So, in this case this
will point to the user
object (just because of this line user.userInfo()
).
Output -
So..... what if we try to invoke the method standalone by storing it in a variable? What do you think would happen? π€π€ Let's check that out.
2) Storing a method in a constant and checking this
const user = {
username : "John Doe",
age : 19,
hobbies : ['Coding', 'Blogging', 'Designing'],
userInfo : function() {
console.log("this is" , this);
}
}
const userDetails = user.userInfo;
userDetails();
Without looking at the output below just think that what might be the output? Will this
going to refer to the window object or the user
object? We have learned that whenever we invoke a function standalone this
will refer to the window object.
Here we're storing our method in a constant so the method will get copied in the constant and when we try to invoke it this
of the userDetails
will refer to the window object and not the user
object.
Output -
3) this
inside an arrow function
const user = {
username : "John Doe",
age : 19,
hobbies : ['Coding', 'Blogging', 'Designing'],
userInfo : () => {
console.log("This is" , this);
}
}
user.userInfo();
Guess the output. Some people might assume that this
will refer to the user
object because we are calling userInfo
as a method. But no, here this
will refer to the window object let's understand why. Unlike normal function definition in an arrow function, the value of this
is lexically scoped, which means that it does not have its own this
context rather it take it from its surrounding. If an arrow function is defined within a function or a method, it will inherit the this
value from its outer function or method.
4) this
in nested function
In our above code snippet we used an arrow function to check its behavior, but how can we fix this behavior? How can we make the arrow function's this
to point to the user
object? Let's check that out
const user = {
username : "John Doe",
age : 19,
hobbies : ['Coding', 'Blogging', 'Designing'],
userInfo () {
innerFunction = () => {
console.log("inside innerFunction" , this);
}
innerFunction();
}
}
user.userInfo();
The innerFunction
is defined inside the userInfo
method so, it will take its this
value lexically which means that it will refer to its parent function's this
, we have invoked the innerFunction
immediately but it is not necessary we can return it too and can use the returned function later.
Output -
Okay so... if we declare an arrow function inside a method then it'll take this
from its lexical scope but what if instead of an arrow function, we have a normal function declaration? Will it going to be point to the user
object or not?
const user = {
username : "John Doe",
age : 19,
hobbies : ['Coding', 'Blogging', 'Designing'],
userInfo () {
return function innerFunction(){
console.log("inside innerFunction" , this);
}
}
}
user.userInfo()();
In this code snippet, we are returning a normal function declaration inside the userInfo
method and then we're just calling it. If you think that innerFunction
's this
is going to refer to the user
object then you're wrong, because if you remember the value of this
is determined by how the function is invoked. And here innerFunction
will be invoked standalone and not as a method.
If you're confused with the above invoking syntax then you can write it like this as well, both syntax works similarly -
const returned_innerFunction = user.userInfo();
returned_innerFunction();
Output -
Remember that if we have the strict mode ON then the output might be different for some cases. Try all of these code snippets with strict mode ON and observe the output. You might also get errors in some lines but they can be solved easily. So give it a try.
ππ» this
in a constructor function
A constructor function is like a blueprint for creating multiple objects with similar properties and methods, and to create an object using that constructor function we have a special keyword called new
. When we use new
keyword to create an object it does some important things behind the scenes.
It creates a new empty object.
It sets the value of that newly created object as the value of
this
inside the constructor function. (basically binds the object with the function'sthis
).Adds the properties and methods defined in the constructor function to the object.
Finally returns the object.
function CreateUser (username, age, email) {
this.username = username;
this.age = age;
this.email = email;
this.getUserName = function () {
console.log(`Username is ${this.username}`);
}
}
const john = new CreateUser("John Doe" , 19, "xyz@gmail.com");
const jane = new CreateUser("Jane Doe" , 19, "xyz@gmail.com");
console.log(john);
jane.getUserName();
In the above code snippet we have used a constructor function to create two objects. And each time we are invoking the constructor function using the new
keyword it is setting the constructor function's this
value equal to that newly created object, that's why we are using this
to set the properties and method inside the object.
Output -
If you didn't understand the above explanation you can think john
as an empty object and we're setting properties and methods to that object using the dot-notation. Check the code snippet below.
const john = {}; // imagine that the `new` keyword has created this empty object.
john.username = "John Doe"; // imagine john.propertyName as this.propertyName, same for the method part
john.age = 19;
john.email = "xyz@gmail.com";
john.getUserName = function () {
console.log(`Username is ${this.username}`);
}
console.log(john);
john.getUserName();
// Now imagine we need to create 100s of user object it'll be really tiring that's why we use constructor functions.
ππ» this
in a callback
When we pass a function as a callback the value of this
will depend on how the function is invoked. When passing a regular function as a callback, it often behaves as a standalone function, and this
may not point to the expected object.
Let's take an example -
const user = {
username : "John Doe",
age : 19,
email : "xyz@gmail.com",
getDetails () {
setTimeout(function () {
console.log("Timeout executed and value of this is" , this);
}, 1000)
}
}
user.getDetails();
Time to examine the code -
We have an user
object and inside that we have multiple properties and a method. The method is not an arrow function so getDetails
's this
will point to the current object, inside the getDetails
method we are using a timeout
and this timeout has an anonymous function declaration inside that, we're just printing this
value. First, let's just see what's the output and then I'll explain it to you.
Output -
Shocked by the output? or not? some people might and some might not. This code deserves our attention, so let's understand it. When we invoke the getDetails
method the timer would have started and after 1000ms or 1sec the callback that we have passed inside the setTimeout
function is invoked, now this callback is invoked as a standalone function and not as a method. And we know very well that whenever a function is invoked as a standalone function the value of that function's this
refers to the global object. Still Confused? Understand it in this way -
Suppose that our getDetails
method is empty at the beginning and after 1sec an anonymous function that is invoked immediately (IIFE) has come inside the getDetails
method, we have learned that whenever a function is invoked standalone it's this
refers to the global object that's why in this case we're getting output as the window object.
// see the code for method
getDetails () {
// empty now
}
// after 1 second
getDetails () {
(
function(){
console.log("Timeout executed and value of this is" , this)
}
)()
}
// If you're having difficulty then you can understand in this manner.
What's the fix for this? An arrow function? Yes passing an arrow function as a callback inside the setTimeout
will fix this issue. Let's see how -
const user = {
username : "John Doe",
age : 19,
email : "xyz@gmail.com",
getDetails () {
setTimeout(() => {
console.log("Timeout executed and value of this is" , this);
}, 1000)
}
}
user.getDetails();
When we pass the arrow function as a callback it will take the this
context from its lexical scope (its parent function) so, when the callback executes its this
value will be taken from the getDetails
method. Now we'll be able to see this
inside the console. Hope this is clear now π₯³π₯³.
Output -
ππ»this
in the Event Handlers
In the case of callback functions attached to the event listeners, the callback is often invoked in a specific way that sets this
to the DOM element that triggered the event. This is a special behavior in JavaScript and it is known as "context binding". But this behavior is different with an arrow function, the arrow function will still point to the global object because of its nature.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>What is `this`</title>
</head>
<body>
<button id="btn">Click me</button>
<script src="script.js"></script>
</body>
</html>
const btn = document.querySelector('#btn');
btn.addEventListener('click' , function () {
console.log(`Inner text of button is "${this.innerText}"`);
})
In our code example, we have a button and we've attached an event to it and the callback of that event is printing the "innerText" of the DOM element.
The behavior or this
in the event handler is special because JavaScript automatically bound the DOM element with the callback's this
, ensures that we can access the element when the event occurs. But always remember that with the arrow function the case is different.
Output -
Still, if we want to use an arrow function we can do something like this and the output will be the same.
const btn = document.querySelector('#btn');
btn.addEventListener('click', function () {
(
() => {
console.log(`Inner text of button is "${this.innerText}"`);
}
)();
})
ππ» Common this
pitfall and how to avoid them
A pitfall is referred to as a common issue or problem that happens to many people. In this section, we will see the pitfalls of this
keyword.
Global Object Binding -
Pitfall -> In the global context
this
refers to the global object which is the window object but if we're using the node environment then it'll be different. This can lead to unintended variable assignment and global scope pollution.Avoidance -> Be cautious when using
this
in the global context, to avoid polluting the global scope declare variables explicitly.
Callback Functions -
Pitfall -> Callback function may have different
this
value depending on how the callback is invoked. They can even refer to the global object.Avoidance -> Use regular functions when dealing with an event and for creating methods. But if there's a use of any type of Web API that requires a callback and is inside a method (for example using setTimeout inside a method) then use an arrow function as the callback function.
Constructor Functions -
Pitfall -> When initializing an object using a constructor function, failing to use
new
keyword can leadthis
to refer to the global object instead of the new instance.Avoidance -> Always use
new
keyword when creating a new instance using a constructor function. This ensures thatthis
will point to the new object.
Arrow Functions -
Pitfall -> Arrow functions capture
this
from their lexical context, this might give unexpected behavior sometimes when using within methods or objects.Avoidance -> Be mindful when using the arrow function in the object method, use a regular function instead of an arrow function for creating methods. But if there is another function inside the method then in this case use the arrow function for the inner part because an arrow function takes
this
from its surroundings.
β Summary
this
is a special keyword that refers to an object.this
is primarily associated with functions and methods, whether they are constructor functions, callbacks or event handlers. Value ofthis
is dynamic and is determined by how the function is invoked.In global context
this
refers to the window object or if you're running your code in the node environment then it'll be different.If a function is invoked standalone then
this
will refer to the global object.If there's a method that is created using function definition then that function's
this
will point to that particular object in which the method is present.Arrow function don't have their binding with
this
, they takethis
from their lexical surrounding.When using a setTimeout inside a method use an arrow function as the callback because the arrow function will take
this
from its parent function which in this case will be the method itself.If we have events then use the regular function as the callback there, because JS bound the callback function with the DOM element automatically. So if we use an arrow function there instead of the regular function then it won't work because arrow functions don't have their binding with
this
.
Thank you so much for reading my article π₯³, hope you have learned something new today. If you have any questions or have any feedback for me to improve my article, please feel free to use the comment box. See ya in my next article ππ»ββοΈ.
Subscribe to my newsletter
Read articles from Gaurav Goswami directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Gaurav Goswami
Gaurav Goswami
Hello World ππ»ππ» I am Gaurav Goswami a MERN stack developer based in India. I like learning about new tech and currently sharpening my skills by learning microservice architecture. Have a good day :)