In this article, we will analyze the dynamic behavior of the this
keyword and learn what it points to when used in different scenarios. But first, let's have a high-level understanding of the concept:
- Each execution context has its own
this
keyword. - It never points to the function in which it is used.
- It behaves slightly differently in strict and non-strict modes, as well as in the browser and Node.js environments.
Now, let's take a closer look at what the this
keyword points to in various scenarios.
In the global scope
The this
keyword, when used in the global scope, points to the global Window
object, as you can see in the following example:
console.log(this); // Window
In a regular function
When using JavaScript in strict mode, the this
keyword in a function declaration or expression results in undefined
.
'use strict';
function welcome() {
console.log(this); // undefined
}
welcome();
However, when using JavaScript without strict mode, the this
keyword in a function declaration or expression points to the global Window
object.
function welcome() {
console.log(this); // Window
}
welcome();
In an arrow function
The this
keyword behaves slightly differently in an arrow function and points to the parent scope of the function.
const welcome = () => {
console.log(this); // Window
};
welcome();
As you can see in the above example, the this
keyword points to the global Window
object, which is the parent scope of the function. For this behavior, the this
keyword in an arrow function is referred to as the lexical this keyword.
Along with the behavior of the this
keyword, there are a few other differences between a regular and an arrow function. I have an article on Regular function vs arrow function in JavaScript if you are interested in learning the differences.
In a constructor function
The this
keyword, when used in a constructor function, points to the object that is created using it.
function Person(name, age) {
console.log(this); // Person {}
this.name = name;
this.age = age;
console.log(this); // Person { name: 'John', age: 30 }
}
new Person('John', 30);
As you can see in the above code, when a constructor function is called with the new
keyword, it creates an empty Person
object and sets the object as the value of the this
keyword.
As a result, we can then use the this
keyword to create properties (e.g., name
and age
) within the object and assign their values with the provided arguments.
In an object method
When a regular function is used as an object method, the this
keyword points to the object that calls the method.
const john = {
name: 'John',
greet() {
console.log(`Good morning ${this.name}`); // Good morning John
},
};
john.greet();
In the above code, the this
keyword within the greet
method points to the john
object. However, the behavior changes when we borrow the method from john
to another object matt
.
const john = {
name: 'John',
greet() {
console.log(`Good morning ${this.name}`);
},
};
const matt = {
name: 'Matt',
};
matt.greet = john.greet;
matt.greet(); // Good morning Matt
Here, the this
keyword now points to the matt
object. Do you notice the difference? It is a subtle but important distinction to keep in mind.
On the other hand, if you change the method from a regular function to an arrow function, the this
keyword will, as usual, point to its parent scope, as demonstrated in the following example.
const john = {
name: 'John',
greet: () => {
console.log(`Good morning ${this.name}`); // Good morning undefined
},
};
john.greet();
Here, as an object literal doesn't create a scope in JavaScript, the parent scope of the greet
method is the global Window
object, which doesn't have a property name
. So this.name
results in undefined
.
In the DOM
The this
keyword in a regular event listener function points to the DOM element, as shown in the following example:
function handleClick() {
console.log(this); // <h1 id="title">Learning the this keyword</h1>
}
document.getElementById('title').addEventListener('click', handleClick);
On the other hand, in an arrow event listener function, the this
keyword, as usual, points to its parent scope, which in this case is the global Window
object.
const handleClick = () => {
console.log(this); // Window
};
document.getElementById('title').addEventListener('click', handleClick);
It's worth noting that in the browser environment, where the this
keyword points to the global Window
object, in the Node.js environment, it points to an empty object, except for a function declaration or expression in non-strict mode, where it points to the Node.js Global
object.
There you have it! I hope you've learned a thing or two from this article. Writing some code and experiencing the behavior for yourself will further solidify your learning.
The this
keyword is one of three topics of a JavaScript execution context. If you are interested in learning the other two topics, I recommend checking out the articles on Exploring the scope and scope chain in JavaScript, as well as Understanding the variable environment and hoisting in JavaScript for further insights.