Understanding the this keyword in JavaScript

Understanding the this keyword in JavaScript

The this keyword is a very important concept in JavaScript. However, it has always been a tricky topic to comprehend especially for new JavaScript developers as well as developers with experience in other programming languages. Although this is a bit of a complex topic, it is also one that appears as soon as you begin writing your first JavaScript programs. You will encounter it whether you are trying to access elements on the DOM, build classes in Object-oriented programming or use properties and methods of regular objects. In this article, we are going to take a deep dive into what the this keyword is all about. With this objective in mind, let's get right to it.

In JavaScript, this is a reference to an object. The object this refers to will vary depending on how and where this is being called. The object that this refers to varies implicitly based on whether it is global, on an object, or in a constructor, and can also vary explicitly based on usage of the Function prototype methods bind, call, and apply.

For the remainder of this article, our main focus will be to learn what this refers to implicitly based on context, and we’ll learn how to use the bind, call, and apply methods to explicitly determine the value of this. So let's go!

Implicit Context

There are four main contexts in which the value of this can be implicitly inferred. These contexts are;

  • the global context

  • as a method within an object

  • as a constructor on a function or class

  • as a DOM event handler

Global Context

If we call this by itself, meaning not within a function, object, or whatever, it will refer to the global window object. When you’re working in a browser, the global context will be window meanwhile if you’re working in Node.js, the global context is global. If you log the value of this in your browser console without any other code, you will see what object this refers to.

console.log(this)
// Output
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

You can see that this is set to the window object which is the global object of a web browser.

You have probably learned that JavaScript functions have their own context for variables. You might be tempted to think that this will follow the same rules inside a function, but it doesn't. A top-level function will still retain the this reference of the global object. In the example below, we are going to log the value of this in a top-level function:

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

printThis()

//Output

Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

This shows that even within a function, this still refers to the global object. However, this is not the case in strict mode. When in strict mode, the value of this in the global context is undefined

Generally, it is safer to use strict mode to reduce the chances of this having an unexpected scope. Someone will rarely want to refer to the window object when using this.

As an object method

Methods are actions that can be performed on objects or tasks that an object can perform. A method uses this to refer to the properties of the object. Let's see an example:

const cameroon = {
  name: 'Cameroon',
  language: ['English', 'French'],

  describe() {
    console.log(`The official languages of ${this.name} are ${this.language[0]} and ${this.language[1]}.`)
  },
}

cameroon.describe()

//Output: 
"The official languages of Cameroon are English and French."

A Class Constructor

A constructor on a class acts the same as a constructor on a function.

class Country {
  constructor(name, yearofIndependence) {
    this.name = name
    this.yearofIndependence = yearofIndependence
  }

  describe() {
    console.log(`${this.name} gained independence in ${this.yearofIndependence}.`)
  }
}

const cameroon = new Country('Cameroon', 1960)

cameroon.describe()

this in the describe method refers to the instance of Country, which is cameroon.

A DOM Event Handler

In the browser, there exists a special this context for event handlers. When using this in an event listener, this will refer to the DOM element that fired the event. In an event handler called by addEventListener, this will refer to event.currentTarget. More often than not, developers will simply use event.target or event.currentTarget as needed to access elements in the DOM, but since the this reference changes in this context, it is important to know.

In the following example, we’ll create a button, add text to it, and append it to the DOM. When we log the value of this within the event handler, it will print the target:

const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)

button.addEventListener('click', function(event) {
  console.log(this)
})

//Output
<button>Click me</button>

Explicit Context

To complicate matters a little more, javascript provides three native methods that can be used to manipulate the way the this keyword behaves. These methods are call ,apply and bind . It is a little bit tricky to know exactly when to use any of these methods as it will depend on the context of your program. bind is particularly useful when you want to use events to access properties of one class within another. Suppose you have separated your code into various parts that each performs their own specific actions, you can use bind to explicitly infer the object you want this to refer to. Now, it's time for us to learn how to use the three native methods call , apply and bind to explicitly determine the object this refers to.

Call and Apply

With call we can invoke a method passing an owner object as an argument. In simpler terms, we can call a method indicating to which object the this keyword will refer to. Let's see an example to help us understand it better:

const person1 = {
    firstName: 'Francesco',
    lastName: 'Sanchez',
    sayName: function() {
        return this.firstName + " " + this.lastName;
    }
}

const person2 = {
    firstName: 'Raul',
    lastName: 'Jimenez'
}

console.log(person1.sayName.call(person2));
//Output
"Raul Jimenez"

Here we have two objects. Each with its firstName and lastName properties, and then person1 object has a sayName method.

Then we call the person1 sayName method in the following way: person1.sayName.call(person2).

By doing this, we're indicating that when the sayName method executes, the this keyword won't refer to the object that "owns" the method (person1) but to the object we passed as parameter (person2). As a result, we get the output we have seen above in our console.

Note: If the given method accepts arguments, we can pass them as well when we invoke it with call as seen in the example below.

const person1 = {
    firstName: 'Francesco',
    lastName: 'Sanchez',
    sayName: function(city, country) {
        return this.firstName + " " + this.lastName + ", " + city + ", " + country;
    }
}

const person2 = {
    firstName: 'Raul',
    lastName: 'Jimenez'
}

console.log(person1.sayName.call(person2, "Bogota", "Columbia"));
//Output
"Raul Jimenez, Bogota, Columbia"

The apply method works very similarly to call. The only difference between them is that call accepts parameters as a list separated by colons, and apply accepts them as an array.

So if we want to replicate the same example using apply we'd have to do it like this:

const person1 = {
    firstName: 'Francesco',
    lastName: 'Sanchez',
    sayName: function(city, country) {
        return this.firstName + " " + this.lastName + ", " + city + ", " + country;
    }
}

const person2 = {
    firstName: 'Raul',
    lastName: 'Jimenez'
}

console.log(person1.sayName.apply(person2, ["Bogota", "Columbia"]));

Bind

In the same way as call and apply , the bind method indicates the object to which the this keyword will refer when a given method executes.

But the difference with bind is that it will return a new function, without executing it. While with call and apply the function is executed right away, using bind we must execute it separately.

Let's see this in an example:

const person1 = {
    firstName: 'Francesco',
    lastName: 'Sanchez',
    sayName: function() {
        return this.firstName + " " + this.lastName;
    }
}

const person2 = {
    firstName: 'Raul',
    lastName: 'Jimenez'
}
const sayPerson2Name = person1.sayName.bind(person2)

console.log(sayPerson2Name())

//Output
"Raul Jimenez"

Arrow Functions

Arrow functions do not have their own this binding. Instead, they go up to the next level of execution. If you want to learn more about Arrow functions and how this behaves in Arrow functions, check out this article.

Conclusion

We have seen in detail how the this keyword works in JavaScript, and how to implicitly and explicitly infer the object this refers to. I hope you enjoyed reading this article as much as I enjoyed writing it. Hoping to bring more JavaScript-packed articles to you soon. Stay blessed.