this
in JavaScript was the most confusing thing in my web development career since I touched JS for the first time. Even now, I use to re-validate my knowledge quite often.
In this article, we will explore the different ways in which this
can be used in JavaScript and how we can control its value to create more maintainable and readable code.
I hope this article will be helpful for beginners in the JS world who might get scared of this little monster and for experienced developers to refresh their knowledge as I do.
Where We Use THIS in JavaScript
We often use the this
keyword in JavaScript when working with object-oriented programming and event listeners. In general, it refers to an object or any value.
Here and after, I will use objects, but the same applies to classes because they are essentially the same.
For example, let’s say we have a Person object with a name property and a greet method:
// Object usage <--------------
const Person = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
Person.greet();
// Class usage <---------------
class Person {
name = "John";
greet() {
console.log(`Hello, my name is ${this.name}`);
}
}
const person = new Person();
person.greet();
When we call the greet method using Person.greet()
, the value of this
inside the method will refer to the Person object, and the output will be:
Hello, my name is John
Developers can also use this
keyword in JavaScript when working with event listeners. For example, let's say we have a button with an onclick
attribute:
<button onclick="console.log(this)">Click Me!</button>
When the button is clicked, this
keyword inside the onclick
function will refer to the button element that triggered the event. This can be useful when manipulating the button element or its properties.
But there are a lot of nuances…
Let’s take a look at the rules we should stick to working with this
:
Rules for THIS working with…
1. Global Context
When this is used in the global context, it refers to the global object. Thanks cap.
In a web browser, the global object is the window object.
console.log(this === window); // true
2. Functions
Note: Henceforth, in this article, I’ll use the word function
for all functions created with the function keyword, and arrow function
for… guess… you damn right!
When this
is used inside a function, its value depends on how the function is called. If the function is called as a method of an object, then this refers to the object that the method belongs to.
const person = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
}
person.greet(); // Hello, my name is John
If we extract the method from the object and call it as it is, we will get undefined
const person = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
}
const myGreet = greet;
myGreet(); // Hello, my name is undefined
…because, if the function is called without an object context, then this refers to the global object:
function sayHi() {
console.log(`Hi, my name is ${this.name}`);
}
sayHi(); // Hi, my name is undefined (in a web browser)
Note that if you run this code in the browser console, you might have an empty string in return. This is because modern browsers set window.name to “” by default.
Now let’s take a look at arrow functions:
this
in an arrow function will always be the same as this in the surrounding code - where it was created, it inherits context from its ancestor.
const person = {
name: "John",
greet: function() {
// In the function "this" refers to the object "person"
// because we called it with the dot notation down the code.
// The arrow function is created in the function with that context
// So as we call it we get name property
const arrowGreet = () => {
console.log(`Hello, my name is ${this.name}`);
};
arrowGreet();
}
}
person.greet(); // Hello, my name is John
Here are interesting things to get started.
What if we create an object method with an arrow function? Let’s take a look:
const person = {
name: "John",
greet: () => {
console.log(`Hello, my name is ${this.name}`);
}
}
person.greet();
Hello, my name is undefined
, because there is no other context other than global. What would be the value of the context property?
const person = {
context: this
}
Global context — window.
What would be the result of the following code?
const person = {
name: "John",
greet: function() {
const arrowGreet = () => {
console.log(`Hello, my name is ${this.name}`);
};
arrowGreet();
}
}
const myGreet = person.greet;
myGreet();
Take your time… 😄
Alright.
The answer is Hello, my name is undefined
because we called the function without any context, it created the arrow function with a global context that has no name property inside.
We can fix it by slightly changing the code:
const person = {
name: "John",
greet: function() {
const arrowGreet = () => {
console.log(`Hello, my name is ${this.name}`);
};
return arrowGreet; // Here we don't call it immediately but return it
}
}
// Here we call function with the person context
// so arrowGreet receives it on its creation and is returned.
const myGreet = person.greet(); // myGreet is our arrow function with saved context
myGreet(); // we call our arrow function that extracts name property from
// the saved context - person.
As a result, we get Hello, my name is John.
3. Bind, Call, and Apply Methods
In JavaScript, we can use the bind
, call
, and apply
methods to set the value of this explicitly. These methods provide a way to control the value of this
in our functions.
const person = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
const greetPerson = person.greet.bind(person); // Set the value of this to person
greetPerson(); // Hello, my name is John
const person1 = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
const person2 = {
name: "Jane"
};
person1.greet.call(person2); // Hello, my name is Jane
person1.greet.apply(person2); // Hello, my name is Jane
4. Event Context
When an event occurs in the browser, the value of this keyword is set to the element that triggered the event.
<button onclick="console.log(this)">Click Me!</button>
5. new Keyword
When a function is called with the new keyword, a new object is created, and this keyword refers to that newly created object.
function Person(name) {
this.name = name;
}
const john = new Person("John");
console.log(john.name); // John
6. Strict Mode
In strict mode, the behavior of the this keyword is modified to avoid some of the common pitfalls of JavaScript. When this is used in a global context or in a function (not an arrow function) that is not a method of an object, it is undefined instead of referring to the global object. This helps prevent unintended modifications to the global object and makes the code more predictable.
"use strict";
function sayHi() {
console.log(this); // undefined
}
sayHi();
In strict mode, when a function is called with the new keyword, this behaves as expected, referring to the newly created object. This behavior is consistent with non-strict mode.
"use strict";
function Person(name) {
this.name = name;
}
const john = new Person("John");
console.log(john.name); // John
When using methods like bind, call, and apply, strict mode does not change the behavior of this. The value of this is explicitly set by these methods, regardless of whether strict mode is enabled.
"use strict";
const person = {
name: "John",
greet: function() {
console.log(`Hello, my name is ${this.name}`);
}
};
const greetPerson = person.greet.bind(person);
greetPerson(); // Hello, my name is John
In summary, strict mode enforces a more stringent environment for the this keyword, ensuring it is not defaulted to the global object in cases where it might otherwise lead to errors, thus promoting safer and more reliable JavaScript coding practices.
Conclusion
In conclusion, understanding the this keyword in JavaScript is crucial for developing clear and maintainable code, especially when dealing with object-oriented programming and event handling. By mastering the various contexts in which this operates — whether in global, functional, or arrow functions, or when using methods like bind, call, and apply—developers can better predict how their code will behave. Additionally, being aware of nuances such as the effects of strict mode and the new keyword can further enhance code reliability. Familiarity with these concepts allows both beginners and experienced developers to harness the full potential of JavaScript's dynamic capabilities.
END OF LINE ▋
Top comments (0)