JavaScript can be a little tricky at times as compared to other languages. This relates to the fact that JavaScript deals with scoping and binding mechanisms differently. To make it easier, it provides the this keyword. It’s a very powerful tool in JavaScript to evaluate the values of different objects or variables at different scoping levels. Here I’m going to explain that with two quick examples.
Example 1
Consider the following piece of code:
function sayHello() {
console.log('Outer hello!');
}
class Greet {
sayHello() {
console.log('Hello from class!');
}
sayClassHello() {
this.sayHello();
}
sayOuterHello() {
sayHello();
}
}
const greet = new Greet();
greet.sayClassHello();
greet.sayOuterHello();
Code language: JavaScript (javascript)
Now let’s try to understand this code a bit. I’ve declared a global function named sayHello at line 1. I’ve then created a class named Greet and declared a function with the same name again. But this time the function is inside the class. I’ve then defined another function called sayClassHello(), which simply calls this.sayHello(). And one more function sayOuterHello() which calls sayHello() directly. With no priop knowledge, what should you think the output of the above code be?
The output would be:
Hello from class!
Outer hello!
Code language: JavaScript (javascript)
This is because, when I called greet.sayClassHello(), the function calls this.sayHello(), that is, the sayHello() function residing inside the class. It’s because at this scope, this refers to the object of the class Greet. So when I invoked this.sayHello(), here this refers to the ‘greet‘ object we created from class Greet.
On the other hand, when I called greet.sayOuterHello(), the function just calls sayHello() without any object accessor such as this. Thus, ‘Outer hello!’ is printed.
Now to get a deeper understanding, here’s another example.
Example 2
Here’s a piece of code I’ve written:
class Animal {
setColor(color) {
this.color = color;
}
getColor() {
console.log(this.color);
}
}
const dog = new Animal();
dog.setColor('Brown');
dog.getColor();
Code language: JavaScript (javascript)
I’ve simply defined a class named Animal with a setter named setColor and a getter named getColor. Later, I’ve created a instance of the class by the name dog. Looking at the code, what do you think should be the output?
The output would be ‘Brown’, that’s it. This is because when I call the dog.getColor() method, it’s going to print this.color, that is, dog.color, which I’ve already set on line 12.
Now, consider the above class as is, but I’m changing how we’re initialising the objects:
const dog = new Animal();
dog.setColor('Brown');
const colorGetter = dog.getColor;
colorGetter();
Code language: JavaScript (javascript)
So what difference did I make? Instead of getting the color using the dog object, I’ve mapped the function dog.getColor() to another instance called colorGetter. Now, if I invoke the function colorGetter(), would I still get ‘Brown’? The answer is no. And here’s why: We cannot directly get a class based function to work independently of objects without an accessor. That is, the moment we create a direct instance of the function inside a class, a reference to the actual object is not created. At this point, this is actually undefined. So, you’re going to see an error that looks like:
TypeError: undefined is not an object (evaluating 'this.color')
Code language: JavaScript (javascript)
To solve this problem caused by undefined this, we can bind the getColor() function to the class using a constructor as follows:
Now, the fixed code should look something like this, and you’d again get the result ‘Brown’:
class Animal {
constructor() {
this.getColor = this.getColor.bind(this);
}
setColor(color) {
this.color = color;
}
getColor() {
console.log(this.color);
}
}
const dog = new Animal();
dog.setColor('Brown');
const colorGetter = dog.getColor;
colorGetter();
Code language: JavaScript (javascript)