What's 'this' (in JavaScript)
We have created objects in JavaScript by using classes and functions. And we have used the 'this' keyword. But what actually is it? And why is it infamous for gotchas? When I first got to 'this' I literally got so frustrated that I concluded JavaScript doesn't deserve to be called a language - because language has rules ! Especially programming languages. And 'this' was like trying to memorize exceptions in chemistry. Except- we humans in any field are not satisfied with exceptions. We will find reasons and theories but never let an exception remain an exception. Some might say - there are somethings that even we humans cannot explain. But I'd like to quote a favorite character of mine :
Its not like that there are things we cannot explain. We search for rules behind the things we don't understand. That painful process is what is called science.
-Senku Ishigami (Dr Stone)
So we will try to uncover the hidden mechanisms of 'this' and try to understand what behavior exists and why it is actually not scary at all.
What is 'this'?
Alright, leave JavaScript, or even computers for a second. Suppose I say a statement to you .
"Hey bro, this".
I am sure you will be like - 'this' what? Road? Sky? What Air?? You don't get it right - what do I mean by 'this' . Look at yourself. You are such an intelligent person studying this article trying to learn JavaScript - and even you don't know what do I mean by 'this'. Then how does one expect their computer to know what 'this' is! I mean 'this' has to point to something. In fact you cannot use 'this' without referring to something- can you? Here I am talking about plain English for now.
Now lets overlap reality with code. Where do they overlap ?- or should I say - made to overlap? Objects! Objects are what we modeled in JavaScript based on reality. In English we would have referred to something - some object ? "Look at this stone" or "Look at this river".
Now lets look in deeper. In the statement - we are associating an activity with an object. And that is the essence of 'this'.
this in JavaScript is also the same. It is used to refer to an object in a specific context usually in a function (activity).
Talking alone won't make it clear. Lets make the understanding sharper.
const obj = {
name : "Parikar",
printName: function () {
console.log(this.name);
},
}
console.log(obj.printName());
Now this code will print the name because it refers to the object as it is.
Now its important to understand this more technically.
this will only bind to an object if its explicitly told to. if a function is told to use 'this' but it is not called in connection to any object it will not understand what it is talking about . In non-strict mode 'this' refers to global object (it goes philosophical everything is one so this is everything) or in strict mode it prints undefined (when told to remain technical).
If you understand this concept than it will be simple to understand the output of the following code:
const obj = {
name : "Parikar",
printName: function () {
console.log(this.name);
},
}
obj.greet = function(){
console.log(this.name);
}
obj.greet();
Here it will work and give output as the name. This is because 'this' is defined during runtime and is bound to the object which calls it.
Look at this interesting snippet of code.
const obj = {
name: 'Tanjiro',
age:16,
printName: function(){
console.log(this.name);
},
}
const objfn = obj.printName;
objfn();
// prints undefined / global object
Here we will get undefined because the object is called directly and not via an object so 'this' has nothing to bind to. Hence we will get undefined.
'this' in classes
The most important use case of 'this' happens in classes or functions that help us define objects.
class Human{
constructor(name,age)
{
this.name = name;
this.age = age;
return this;
// will return 'this' even if we don't return explicitly
}
}
const person = new Human(`Tanjiro`,0);
Here 'this' will refer to the object that is created. This is not an exception but part of the job of the new keyword. Now since it returns 'this' - person points to the newly created object in memory. And please understand that here we are doing no magic. person binds to 'this' because the expression
new Human(`Tanjiro`,0);
returns it. If it returned some other object then we would have that as output. (If it was a primitive it would have been ignored)
Many wish 'this' could be only this much but in comes arrow functions. This is what scares people the most. And honestly it scared me too. The problem was that I didn't understand functions not the 'this' keyword. So before talking about 'this' in arrow functions lets talk about - well - functions.
Here we will not discuss functions much - but more of how functions are stored. Functions will have a detailed blog. Now the information that I want to cover is simple - when a function is stored in the memory it is stored as: function definition + a reference to its surrounding scope. This means that if we have some data in a scope and the function uses it while being inside that scope that function on being called can still access the data. If the outside scope is destroyed but the function remains than it will have kept the value of the data it referenced stored. This is called closure.
Now with this in mind we will proceed to arrow functions use of this.
'this' and Arrow Functions
Before understanding 'this' in arrow functions we first need to understand 'this' in normal functions. Whenever you can define a normal function in a way that it will have a valid this - one must understand that the 'this' is not defined until that code actually executes. Which means that the 'this' keyword is defined during runtime and is dynamically made to bind with that method. This doesn't seem to make a difference but is crucial in understanding 'this'.
Now for arrow functions - they don't have their own 'this'. Instead they inherit 'this' from their scope.
This is the statement we need to adhere to. But its not that easy to understand - is it?
What the statement is saying is that when normal functions run - a 'this' is resolved for them. If they are not called in a manner that causes them to have 'this' , they will have undefined or global object (in strict and non-strict mode respectively) . But for arrow functions nothing like that happens. But they can still use 'this'. Except - its not theirs. Its borrowed from the surrounding scope, If its surrounding scope too doesn't have 'this' then it is a different thing it will return undefined(strict)/global object(normal).
const obj = {
name: 'Tanjiro',
printName: ()=>{
console.log(this.name);
},
}
obj.printName();
Here it prints undefined because its own 'this' was not evaluated. In the surrounding scope there is nothing like 'this' (it is resolved for functions called via objects and is not present in the object itself).
Now take a peek at this code.
function template(name)
{
this.name = name;
this.printName = ()=>{ console.log(this.name); },
return this;
}
const fnExtract = new template(name);
fnExtract();
Here it is often expected by people that the output will be undefined but we must remember when a function is stored it stores its lexical environment reference along with it. And this inherits from the lexical environment, Which means that 'this' here for this function was established during the time of definition itself (bound when new ran and initialized it) and hence has proper value.
In fact here:
const obj = {
name: 'Tanjiro',
printName: ()=>{
console.log(this.name);
},
}
obj.printName();
It returns undefined.
But in this code:
const obj = {
name: 'Tanjiro',
age:16,
outer: function() {
const printName= ()=>{
console.log(this.name);
}
return printName;
},
}
const innerfn = obj.outer();
innerfn();
The output here will again be proper print name being printed. Because again 'this' was bound to the arrow function in its lexical environment and is now accessible for it.
Now that 'this' keyword is done - we can understand call() , apply() and bind().
What are these methods? These methods just bind an object as 'this' to the function they are calling.
Like instead of calling a function directly as :
funct() we do funct.call() or funct.apply() or funct.bind() to get specific behaviors.
Lets take an example.
function adder(a,b){
return a+b+ this.c;
}
Now to make it work properly we need to call it via an object that has the value c. So we will have to first create an object and then call it. We can do:
const applyAns = adder.apply( {c:10,d:20} , [ 5 ,10 ] );
We passed the {c:10,d:20} object as 'this' and passed on 5 and 10 as arguments. The difference? Apply passes arguments as array and callAns passes arguments as normal parameters while the first parameter is always the 'this' binding. If an object doesn't exist it converts the first parameter into one and uses it.
And what is bind?
Bind is generally used to store this function call and execute it later. Unlike the previous two which directly return an answer , bind returns a reference to the function which will make the same function call as call or apply and return the same output. It might be really useful when we want to execute it in async mode.
const bindFn = adder.bind({c:10,d:20} , 5 ,10);
const callAns = adder.call({c:10,d:20} , 5 ,10);
//after some time;
const bindAns = bindFn();
Here we can see the use of bind.
Once 'this' keyword gets clear - it isn't very difficult to deal with object oriented programming in javascript. When one understands the internals - no gotcha can scare them. So don't be scared . Look for the rules. Understand the concept. I am not saying this randomly. I am saying this because the device you are reading this on - is the living proof of the fact that we are here today because many people did the same - for us.