DEV Community

ALEJANDRO
ALEJANDRO

Posted on • Edited on

UNDERSTANDING THE "THIS" KEYWORD IN JAVASCRIPT

For someone who starts learning JavaScript as their first coding language, understanding the this keyword is one of the big challenges to be encountered. It took me quite a while to fully assimilate the different use cases of this JavaScript concept. It is one of those core aspects such as scope, closures and hoisting that need a lot of drilling and practice until they sink in for good.

After reading documentation from different sources and following a few tutorials online, I got to understand when and how to use it. Although practice is what made it stick in.

And now let's get into the meat. The this keyword behaves somehow as an object pointer. It points at different things based on how it is called. Unless otherwise specified, this will always point to its parent object. By default, this parent object is the window object, which contains the DOM document. Window is the global object in the browser. However, it is important to note here, that if we run the same function in the terminal using Node.js, i.e the backend JavaScript run-time environment, the default value of this is global object since we are no longer in the browser.

In the following lines I will divide this core javascript concept in four different use cases.

Case 1: When used in combination with bind, apply, and call

When used with the javascript native methods bind, apply or call, the value of this is set explicitly, and it always behaves the same, giving us full control over what we assign as this. That being said, there are several differences between these three methods. And I will explain them in the lines that follow. But what the three of them have in common is that they change the value of this to something else.

Used with bind

bind is a method on functions by default, and so are apply and call. bind returns a copy of the function it's called on, where this is set to the first argument passed into bind.

In the next section we will tackle apply and call, but for now let’s test how this works with bind. First of all, let’s write a simple function that prints out the default value of this. Go to your favourite browser open the developer tools, and open the console to write the function:

function whatIsThis() {
    console.log(this)
}

We are now going to use this function with bind in order to change the default value of this into something of our choice. We will declare a variable, and we will assign to this variable the returned function that results from binding this to the object that we pass as an argument of bind. You can now call the new function. What do you get as an output? What is the new value of this?

var explicitlySetTheValueOfThis = whatIsThis.bind({name: 'Barcelona'});
explicitlySetTheValueOfThis();

bind vs apply and call:

apply and call change the value of this inside the function, just like bind does. The big difference though, is that they also run the new function immediately. As we saw, bind just returns a copy of the function, and then you have to run the new function as a second step.

Something important to note here is that a function returned from bind cannot be bound to a different this value ever again.

apply vs call:

If the function where we use apply and call has no arguments other than the first one -where we pass in the new value of this, then both methods will behave alike. However, if the function where we use apply and call has arguments, apply and call will behave differently.

And here is the difference: when you pass arguments into a function with apply you need to pass them in an array. So the second argument of apply is an array whose items are the arguments that we were going to pass into the original function.

Contrarily to apply, with call we don't use an array, we just pass the arguments into the function one after the other, following the first argument, where we had passed in the new value of this that we wanted to bind with call.

As a result, if we copy and paste the following code into the console, the output will be the same in both cases. The only thing that changes is the way we provide the function with the arguments that it needs.

function logThisWithGreeting(greeting, name) {
    console.log(greeting, name);
    console.log(this);
}
logThisWithGreeting.apply({name: 'Alex'}, ['hi', 'Alex']);
logThisWithGreeting.call({name: 'Alex'}, 'hi', 'Alex');

Case 2: When this appears in object methods.

When a function is called as a method - a function inside an object, this points to that parent object. Let's write some code that represents this scenario. In the same console you previously opened, write now an object which contains a method. All this method should do, like the function we saw in case 1, is to log the value of this. Then call that object method. What output do you get? You should get the object that, when calling the method, was on the left side of the dot. You will not see the name of the object, but you should get an object that contains the method that you just called.

var strangeObject = { 
    myMethod: function(){
        console.log(this);
    }
};
strangeObject.myMethod();

Case 3: When this is used in constructor functions.

When a function is called as a constructor, this points to the object that the constructor is creating. In case you don't have a clear idea of what a constructor function does, here is a video that might enlighten you. But in short, a constructor is a function that returns an object. Factory functions return objects too, but constructor functions are called with the new keyword and they are capitalised. Here is a very good article by Eric Elliot that helped me better understand the difference between constructor and factory functions.

Let's now test this in constructor functions. Open your console, and write a constructor function to create objects with one property, the city name. The function will have one parameter, which will take a string argument, the name of a city. Now, you can assign to a variable called myCity the value returned from running the constructor function with the new keyword. Don't forget to pass in your city as the argument. Here's an example. What output did you get? console.log the variable to see it.

function City(name) {
    this.name = name;
}

var myCity = new City('Barcelona');
console.log(myCity);

Case 4: This and regular functions vs arrow functions

When we explained bind, apply and call in the first use case, we wrote the following function to log the value of this:

// this used in regular functions
function whatIsThis() {
    console.log(this)
}

Regular functions, like the one above, and like the object methods that we saw in case 2, have their own this keyword, which points to the parent object in scope. If the scope of our function at the time it executes is the global object, this will refer to the global object, as specified in the introduction to this article.

However, what if we use an arrow function instead? Arrow functions are tricky because they don't always behave like a regular function, even if we can use them for the same purposes in most cases. One of the things they differ from each other is the this keyword. Simply, arrow functions don't have their own this keyword. This means we can still use this inside an arrow function, but its value will not be the parent object of the function. The value of this in an arrow function is that one of this as used outside the scope of the function.

If we write the example above as an arrow function, the result will be the same, not because this points to the parent object of the function, but because this is the window object. It would be as if we ran console.log(this) in the global scope.

However, and here we will see different results, what if we use arrow functions as an object method?

// global scope

// regular function used as method
var myObjectRegular = { 
    regularMethod: function() {
        console.log(this)
    }, 
}; 

myObjectRegular();
// Expected result:
// {regularMethod: ƒ}

// arrow function used as method
var myObjectArrow = { 
    arrowMethod:() => console.log(this), 
}; 

myObjectArrow();
// Expected result:
// Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}

If we run the code above, for the regular method, the value of this is the parent object of the function. But when running the arrow method, the value of this is the window object. This proves that arrow functions don't have this and that when we use this inside of an arrow function, it points to the value of this as used outside the function.

As a final note, I would like recap an important detail that I mentioned a few lines ago but didn't give much focus. That is, the this keyword gets its value assigned when the function is being executed, not when it's being declared.

Let’s have a look at an example where we grab a DOM element and attach an event listener to it. Where is this going to refer to?

Open the developer tools of your browser, edit the DOM and insert a very simple button with an id attribute:

<button id="myButton">What is this</button>

Then go to the console tab and write the following code, which basically logs to the console the value of this when clicked:

var myObject = {
    logValueOfThis: function() {
        console.log(`value of this: ${this}`);
    }
}

document.getElementById('myButton').addEventListener('click', myObject.logValueOfThis);

Now if we close the developer tools and click on the button, I don't see valueOfThis: {logValueOfThis: f} as the value of this but value of this: [object HTMLButtonElement].

This means the value of this at the time of execution of the callback function was the button object. Whenever we set up an event listener, the event handler function will execute as if it were attached to the element object.

So what will we do to have this pointing to myObject? We will need to rebind our method to myObject by calling bind.

document.getElementById('myButton').addEventListener('click', myObject.logValueOfThis.bind(myObject));

WRAPPING UP

In this article, I tried to introduce in a simple and yet detailed way one of the pillar concepts of JavaScript. I aimed to engage you by testing the cases that I was presenting. And I have provided some support with links to resources in case you need to extend on some of the topics that are mentioned here. Together with the this keyword, concepts such as higher order functions, callback functions, functional programming, constructor functions and factory functions are key to any individual who aims to become a professional JavaScript developer.

On a personal note, I started in programming three years ago, following the decision of a major career change. I am a former translator and language teacher and I had never done any programming before whatsoever. It has not been an easy trip and there are many frustrations that came along the way. But thanks to this experience, I have learnt what things are particularly difficult to understand for someone coming into programming following a non-academic path. If that is your case, and you are taking your first steps, this article was specially written with the aim to help you, even if only in an introductory way.

Top comments (0)