"This" keyword in javascript Arrow function VS Regular function. You need to understand that !?
The difference in this value when using inside the arrow or regular function
1. Why do you need to understand "this" keyword values π΄?
Simply because if you don't, you are properly going to face mysterious errors π€― and waste a lot of time to figure it out and how to solve them, especially when building a huge app.
During the process of web development we faced this keyword many times understanding the main concept of using it and its value will help us to improve the quality of the code and reduce bugs.
2. What you will learn π ?
you are going to learn the following π¨π½βπ»:
- The different values of this keyword.
- How and when using arrow functions instead of normal functions will reduce unexpected errors?
- Will understand the different situations to give a preference to arrow functions over the normal ones or the other side.
3. let's get started β‘οΈ
First things first I would like you to remember two things for later use:
Normal function:
The value of this keyword is based completely on how its function (or method) is called.
Arrow function:
The value of this depends on where that function is located in your code (surrounding context).
Different values of this π:
- this could be any of the following:
1. A new object:
this value could refer to a new object if the function (constructor) is called with the new keyword.
try the below code in your browser console and the result is that this value referred to the new object which is called personOne.
// create a class called Person
class Person {
constructor(name, address) {
this.name = name;
this.address = address;
}
}
const personOne = new Person("ahmed", "Egypt");
console.log(personOne);
2. A specified object:
this value could refer to a specified object If the function is invoked with call/apply:
To know more about call/apply functions check this link: call/apply
try the below code in your browser console and the result is that this value referred to a specific object according to the object passed to the call function.
let obj1 = {
firstName: 'John',
lastName: 'Smith'
};
let obj2 = {
firstName: 'Ann',
lastName: 'Brown'
};
function sayWelcome(greeting) {
console.log(greeting + ' ' + this.firstName + ' ' + this.lastName);
}
sayWelcome.call(obj1, 'Welcome'); // Welcome John Smith
sayWelcome.call(obj2, 'Welcome'); // Welcome Ann Brown
3. A context object:
this value could be pointed to the same object if the function is a method of an object:
try the below code in your browser console and the result is that this value referred to the same object.
class User {
constructor(id, username) {
// Properties
this.id = id;
this.user = username;
}
// Methods
writeMsg() {
return `Hello ${this.user} Your Id Is ${this.id}`;
}
}
const newUser = new User(1, "sanad88");
console.log(newUser.writeMsg()); // Hello sanad88 Your Id Is 1
4. The global object or undefined:
this value could be pointed to the global object or undefined If the function is called with no context.
try the below code in your browser console and the result is that this value referred to the global object during the first time and the second time will be undefined.
console.log(this.alert("Hi There"));
console.log(writeMsg()); // undefined
After we knew this different values Now let's revisit the first section:
How and when using arrow functions or regular functions?
Let's have an example to clarify the concept:
// constructor
function Person() {
this.age = 0;
}
// adds a method to increment the age.
Person.prototype.incrementAge = function () {
setTimeout(function () {
this.age++; // increment the age by one
console.log("age increased by one!");
// console.log(this.age); // NaN
}, 500);
};
const someOne = new Person();
someOne.incrementAge();
console.log(someOne.age); // 0
Once you run the above code inside the browser console you will find that someOne.age returned 0, despite calling the incrementAge method which should increment the age of someOne by one, But that does not happen !!! π€
Now did you still remember what happened to this value using the regular function?
Yes, You did ππ» π₯³.
- let's dive into the code above:
The function passed to the setTimeout is called without new keyword, without call() or apply() and without a context object. That means the value of this inside the function belongs to the global object and NOT the someOne object. So what happened was that a new age variable was created (with a default value of undefined) and was then incremented (undefined + 1 results in NaN).
How to solve this problemπ‘?
There are two solutions to avoid going through that error:
- By Using closure:
Person.prototype.incrementAge = function () {
const objRef = this; // using a reference to the object saved in a variable and use that variable.
setTimeout(function () {
objRef.age++; // we used here the variable not this.
console.log("age increased by one!");
console.log(someOne.age); // 1
}, 500);
};
const someOne = new Person();
someOne.incrementAge();
By only assigning this to a variable in the outer function which already had the access to this like we said before and using this variable will avoid the mistake that happened before.
- Using the arrow function:
Now this is the right time to remember the section about using the arrow function makes The value of this depends on where that function is located in your code.
To make it simple let's say while using the arrow function to know the value of this check what is the value of this inside the outer scope and will inherit the same value to it.
// constructor
function Person() {
this.age = 0;
}
Person.prototype.incrementAge = function () {
// Using the arrow function down below.
setTimeout(() => {
this.age++;
console.log("age increased by one!");
console.log(this.age); // 1
}, 500);
};
const someOne = new Person();
someOne.incrementAge();
// after running the function we can print the someOne object.
console.log(someOne);
Only a small quiz to make sure you understand the concept try to solve the below question without checking the solution:
What will happen if we change the incrementAge method to an arrow function and keep all remaining code?
// constructor
function Person() {
this.age = 0;
}
// using the arrow function all over the planet π
Person.prototype.incrementAge = () => {
setTimeout(() => {
this.age++;
console.log("age increased by one!");
console.log(this.age); // ??
}, 500);
};
const someOne = new Person();
someOne.incrementAge();
// after running the function we can print the someOne object.
console.log(someOne.age); // ??
Excellent job ππ»ππ»ππ»
Exactly, what happened here is this inside setTimeout function will inherit the this from the outer function which used the arrow function too, and this here inherits the this value from the outer scope which is the global object and the same problem happened like before.
4. Summary π§Ύ:
this keyword can refer to:
- A new object
When using the new keyword.
- A specified object
When using call() or apply().
- A context object
When a function or method is an object method.
- A global object or undefined
When the function is called with no context.
NOTE π
When using the arrow function will inherit the this value from the outer scope but when using the regular function will use this value according to how this function is called.
π Thanks so much for the time you spent reading the simple article π
Please
feel free to modify and/or add any information to the article if you find any mistakes.