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.