What is This? (in JavaScript)
We all developers have atleast once in our lifetime encountered the this
keyword in many Programming Languages (self
if you are from Python
island). The implementation of this
varies somewhat in all these languages, but it is not as confusing as it is in JavaScript.
Detour to C++
Before starting, lets see another famous programming language that utilizes the this
keyword, C++
.
The example here is true for most other languages as well
I think the implementation of this
in C++
is as straight forward as it gets. It represents the current instance of the Class.
Let's See an Example
#include<iostream>
class Person{
public:
std::string name;
Person(std::string name){
this->name = name;
}
};
int main(){
Person p = Person("Suparth");
std::cout << p.name;
return 0;
}
Here, we see that parameters in the constructor function have same name as the public variable names in the class. However, this
makes it clear on which variable is the value being assigned to. Here this
refers to the instance of the class.
this->name = name;
It means the value of variable name
from parameter is being assigned to the variable name
from the class instance.
Thus, while logging the value of name
from object p
, we get Suparth in terminal.
Doesn't Seem Complex does it? Well, buckle up, as we gear towards the island of JavaScript
where everything is weird. Specially This
!
Welcome to JavaScript
JavaScript
's this
has the same heart as other programming languages. It too refers to the current instance. But, the catch here is WHAT IN HELL DOES CURRENT INSTANCE MEAN?
Current Instance / Scope
JavaScript (by default) is a globally scoped language. It means you dont need to create any extra scope to execute code like in C++ or Java where you require a main
method. Here you can execute code from first line itself. It is similar to Python in this regard. To achieve this, JavaScript has scopes/contexts where everything is executed.
Scopes in JavaScript:
- Global Scope
- Function Scope
- Block Scope
We wont be going in what these scopes mean, as they are entirely different topic. Let's focus on this
for now.
Some extra topics that you can learn that are based around Scopes in JavaScript
- Hoisting
- Closures
This in JavaScript
this
refers to different things in JavaScript.
The basic gist of this
in JavaScript is that this
refers to the object of whatever scope is wrapping it. If the object of scope is not found, it refers to the parent's scope's object.
Confused? Let me elaborate.
We see that window
is an object in JavaScript which is the object that represents the global scope. Block scope doesn't have any object that represents to it. Hence, if this
is referred inside a simple block scope (within curly braces), it still refers to the window object as there is no other object for it to bind itself to.
By {}, I mean simple lexical scoping or using if statements, loops etc.
This Refering to Global Object
- Global Scope
console.log(this);
- Block Scope
if (true) {
console.log(this);
}
- Function Scope
function fn() {
console.log(this);
}
fn();
- Inside an Object
let obj = {
thisKey: this,
};
console.log(obj.thisKey);
All these examples will print the window object in console.
Everything here has been explained by the paragraph above, except for last two examples of function and the object. The obvious question here is doesn't function have its own scope? And why does this
not bind to Object?
Function:
JavaScript functions are weird as well as magical. We can not only treat them as normal subroutines by simply calling them, but also as constructors by invoking them using the new keyword! Here, function fn
has just been called like a normal subroutine. Thus, no constructor is created which does not create a function scope, but creates a local scope. And since no object corresponds to local scope, it looks at the parent scope of the function which is global scope. Since the corresponding object of global scope is window, this
inside a function invoked by a simple function call refers to the global scope or window object.
Object:
The answer to Object's example simple. Creating new object does not create any new scope, as obj
is simply a variable (even though it uses curly braces). Thus, it refers to the global scope/window object as obj
is created inside global scope.
The Prototype Carnival
Before moving forward, we must be familiar with prototypes and prototipal inheritance. I wont go much in detail, but here is a small brief.
Everything in JavaScript is an Object. If we see the prototype chain of anything in JavaScript, we see it is same as Object.Prototype
. The Prototype of Object is null. Hence, in the top level of hierarchy, we see object being present.
This means function is an object too. If you run following code:
Function.prototype.__proto__ === Object.prototype;
We get true as the result.
This Refering to Certain Object
- Functions as Constructors
function ConstructorFunction() {
console.log(this);
}
const newInvoker = new ConstructorFunction();
What is new Here?
We see that the first distinction from normal function call is we invoke the ConstructorFunction
function using new
Keyword. This now creates a function scope within the global scope. When a function scope is created, the corresponding object is Function itself (function is an Object in JavaScript - discussed above). Hence, the this
keyword will now refrence the function itself.
A more sophisticated example
function Game(initialScore) {
this.score = initialScore;
this.increaseScore = () => {
this.score++;
};
this.decreaseScore = () => {
this.score++;
};
}
const newGame = new Game(0);
console.log(newGame.score);
newGame.increaseScore();
console.log(newGame.score);
The Output is:
0
1
- Arrow Functions
const arrFunction = () => {
console.log(this);
};
const arrFnNewInvoke = new arrFunction();
This will result in an error that says arrFunction is not a constructor
.
Can you say why?
Arrow functions are not meant to be declared as constructors. Thus, they cannot be invoked using new keyword. Which means this
keyword inside arrow functions will always point to the wrapper around the arrow function. In this case, it is the global scope/window object.
- Function as Method inside an Object
let obj = {
name:"Suparth"
getName:function(){
console.log(this)
}
}
obj.getName();
Remember while discussing the global scope of this
keyword, we said that in normal function call, this
points to global object as the wrapper of the function itself is the global context? Well here the wrapper is the object obj
. What do you think will this
point to in this case?
You might think that as getName
function is wrapped by an object obj
, this
should point to global/window because Object doesnot create any scoping, which results in this
pointing to window itself.
The answer however is far more complex than that. Let's elaborate how objects are created in JavaScript.
let obj = new Object();
obj.name = "Suparth";
obj.getName = function () {
console.log(this);
};
obj.getName();
Current block of code, and the one before this results in same behavior. The only difference is how we define objects, lattar one being a longer way to do so.
Do you see what we did here though? Do you remember we invoking a function using new keyword? Yes!!! This is same as invoking a function as a constructor. This is possible because Function
is an Object
in JavaScript.
Then what will this
inside a constructor function point to? Yup. The constructor function itself! Hence the output here of obj.getName()
will print the object obj
itself.
- Arrow Function as Object Method
let obj = {
name:"Suparth"
getName:()=>{
console.log(this);
}
}
What will this
point to in this case?
If you say window object, you are absolutely correct. As arrow functions cannot act as constructor function, it wont bind to the object obj
, but to the global scope/window object.
Classes - New Kids on the Block
Es6 was an interesting transition from Es5 in JavaScript island. Many new cool kids were added in the island who brought much needded changes that made the language much more redable and accessible. We all know the introduction of Promises
and async/await
, where async/await
were the hot cakes. But well, classes were added too! So? How does this
work in Classes? Well, how it always has! Let me explain.
You see classes in JavaScript is same thing as creating functions and invoking them as constructors using the new
keyword. It just is a syntatic sugar similar to async/await
.
Thus, whatever we discussed before that includes Function
, the concept here holds true.
class Person {
constructor(p_name) {
this.name = p_name;
}
getName() {
console.log(this);
}
}
const P = new Person("Suparth");
Well, you see the similarity? Calling function as a constructor is same as creating a class! It just is an declarative way to do so. This made JavaScript more closer to other Object Oriented Languages and made inheritance
similar to other langauges as prototypes
seemed weird before.
class Game {
constructor(initialScore) {
this.score = initialScore;
}
increaseScore() {
this.score++;
}
decreaseScore() {
this.score++;
}
}
const newGame = new Game(0);
console.log(newGame.score);
newGame.increaseScore();
console.log(newGame.score);
Remember this Example from above? Well this is how we would do it using ES6 way. This proves that classes are nothing but a syntatic sugar to create more redable constructor functions.
Not Convinced? Well, run this code:
class DemoClass {}
console.log(typeof DemoClass);
The Output will be
"function"
Thus, nothing changes while using this
keyword inside classes in JavaScript.
Leaving JavaScript
That is all I have in store for today. this
isn't over by the way! this
has alot more to be explored upon. Did you know, you could modify behavior of this
in JavaScript? Well, stay tuned for more blogs, as more of this
is yet to be discussed.
Adios