What is `this`? 😡

Gaurav GoswamiGaurav Goswami
13 min read

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 thisis 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.

  1. It creates a new empty object.

  2. 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's this).

  3. Adds the properties and methods defined in the constructor function to the object.

  4. 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.

  1. 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.

  2. 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.

  3. Constructor Functions -

    • Pitfall -> When initializing an object using a constructor function, failing to use new keyword can lead this 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 that this will point to the new object.

  4. 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 of this 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 take this 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 πŸ™‹πŸ»β€β™‚οΈ.

0
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 :)