Decoding this Keyword in JavaScript: A Simplified Guide

Photo by sydney Rae on Unsplash

Decoding this Keyword in JavaScript: A Simplified Guide

🔍 THIS has always been confusing for all the developers in the history of Javascript, and I believe no one is untouched by its mighty confusion. I was also once very confused and always tried to avoid this keyword as much as possible and needless to say it cost me a lot when I faced this in interviews. But believe me, it’s super easy when you understand it, and becomes one of the handy ways of writing elegant code in Javascript. We will together unfold the mystery of this keyword in Javascript.

Let’s understand THIS in the easiest way possible.

🚀OK, let’s start by first defining it. This is a reserved keyword in Javascript that references an object. Now you’ll ask what that object refers to.

And this is where things get a little tricky and we get confused. So, basically, this always refers to the object where the function was invoked whose value differs on how and where it was called.

Basically, this keyword stores the value of the execution context where the function was called and it always varies and can be even explicitly changed.

Now let’s understand different scenarios where this works differently.

Different scenarios of this in javascript

Whenever we execute our code in javascript, a new execution context(we will cover this another interesting topic later in another blog) is created in the stack of the JS engine and its behavior always varies on how that function was invoked.

We’ll explore them one by one —

1. Default binding 🌐

Let’s first console this and see what it returns.

console.log(this)

global object as a this

So, we can clearly see when we’re calling this it’s returning the global windows object which is the current execution context of the main function running the console. Just note one more thing in strict mode it returns null because default binding is not allowed.

Now we can say by default this refers to the global window object in default binding. Let’s understand it via code.

var firstName = 'John';
function getName() {
  console.log(`My name is ${this.firstName}`);
}
getName(); // Output: 'My name is John'

Here we can clearly see that firstName was defined under the global windows context as per the default binding of this to global windows, this.firstName returned John.

Let’s get over another example now —

var firstName = 'John';

function getName() {
  var firstName = 'Jane';
  getMyName(); // or this.getMyName()
}
function getMyName() {
  console.log(`My name is ${this.firstName}`);
}
getName(); // Output: 'My name is John'

Here nothing changed, despite the fact that the new function getMyName was called under the execution context of getName() function, and instead of Jane we got John.

When you declare a regular function in JavaScript (like foo and bar), the value of this inside that function is not bound to the function itself or the function's lexical scope. It's determined entirely by how the function is called. But in case these were methods bound to some objects, then the scenario would be entirely different, which we will explore in the next topic, i.e. Implicit Binding.

2. Implicit Binding 🔗

Let’s first go through the code written below —

const obj = {
  firstName: "Rishav",
  getName: function() {
    console.log(`My name is ${this.firstName}`);
  }
}

obj.getName(); // Output: 'My name is Rishav'

Here, I got my name Rishav printed since this was referring to the obj object instead of the global object this time. This is internal binding.

Now you might wonder what happened here. So, let’s dig it a little bit.

Whenever we create any method that is not a regular function, and if this is called within it, then the value of this is bound automatically to the parent object where that particular method was created. In this case, getName() was that method. This is called an Implicit binding of this when called inside methods.

Let’s tweak our code a little bit —

const obj = {
  firstName: "Rishav",
  getName: function() {
    console.log(`My name is ${this.firstName}`);
  }
}

const myName = obj.getName;
myName() // Output: 'My name is undefined'

WHAT THE HECK?????

How come we’re getting undefined here instead of Rishav now?

So, If you read the line const myName = obj.getName;, this is called a function expression. Now, when we wrote this code it created a regular function named myName which has a global execution context and this keyword now points to a global window object abiding by the concept of default binding.

3. Explicit Binding 🎯

Now you might be wondering what if I want to change the reference object where this is pointing to?

Here are those three beautiful methods that make this possible, i.e .bind(), .call(), and .apply().

Using these above methods, you can force the function invocation to use the given particular object as a reference

Let’s take the previous example and fix it to get my name —

const obj = {
  firstName: "Rishav",
  getName: function() {
    console.log(`My name is ${this.firstName}`);
  }
}

const myName = obj.getName.bind(obj);
myName(); // Output: 'My name is Rishav'

And, here we are with my name again being consoled out. Hurrayyy!

Similar to .bind(), we have .call() and .apply(). In the case of these two methods function is immediately called with a new reference of this passed as an argument instead of .bind() where a new function invocation was created.

.call() and .apply() just differ in how they’re called. .call() takes the function arguments as a separated by comma, whereas .apply() takes arguments as an array.

Let’s write one example for these too —

function getMyName(friend) {
  console.log(`Hey ${friend}, my name is ${this.firstName}`);
}

const obj = {
  firstName: 'Rishav',
};
getMyName.call(obj, 'Jack'); // 'Hey Jack, my name is Rishav'
getMyName.apply(obj, ['Joe']); // 'Hey Joe, my name is Rishav'

4. New Binding 🆕

Let’s first write a code and then understand this more clearly —

function getName() {
  console.log(this) // ??
}

const myName = new getName();
console.log(myName)

What do you think, we get as a value of this here?

getName

Surprising isn’t it?

This is something completely new this is referring to.

So, whenever we use new constructor of a function, its prime duty is to create a new empty object in the background and return it. And in this whole process, it sets the reference of this to that newly created object itself.

Let’s see this truth practically —

function details(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.getName = function () {
    console.log(`My name is ${this.firstName} ${this.lastName}`);
  };
}

const myName = new details('Rishav', 'Pandey');
myName.getName(); // Output: 'My name is Rishav Pandey'

Can you see, our getName function is now acting as a method of a brand-new object that has been created by new constructor, and this points to that newly created object itself stored in the myName variable.

I guess, this was it. We understood all 4 different scenarios on how this works when dealing with Javascript. But here’s a catch when it comes to the arrow function, things change completely which we will explore now.

Arrow Function and it’s chemistry with THIS

➡️ In the arrow function, Javascript sets this lexically. That means when it comes to this, the execution context is not taken into consideration and the arrow function inherits the value of this from the outer function where that arrow function was defined.

Let’s look at the code below —

const obj = {
  name: "Rishav Pandey",
  innerFunction() {
    const getName = () => {
      console.log(`My name is ${this.name}`);
    }
    getName()
  }
}

obj.innerFunction() // Output: 'My name is Rishav Pandey'

I get my name printed here, as this inherits the value of the object whose reference was created for the execution context of the innerFunction() function, instead of getName(). Instead, if we had created getName() as a regular function this would have been pointing to a global object. Try this as a homework.

Let’s go through another code here —

const outerThis = this;

const func = () => {
    console.log(this === outerThis);
};

func.call(null);        //true
func.apply(undefined);  //true
func.bind({})();       //true

this will always refer to the lexical scope and not be impacted by any rule from the 4 rules such as explicit binding in the above example.

Summary

📝I hope we were able to decode this mystery in JavaScript and are now ready to explain this beautiful concept to others. Let’s recap things once again -

  1. This value of this generally depends on the execution context of a function.

  2. In general, it will always refer to global objects as a part of default binding.

  3. We can change the reference of this if needed via .bind(), .call(), and .apply().

  4. For arrow functions, rules change a little bit. It’ll inherit the value from the other function where that particular arrow function was created.

I understand the blog went a little bit, but I hope it was worth it and we learned something new together.

Sending my love to all the readers, and do show your love and support and give some claps if you found this really useful.

I will be coming soon again with a new interactive blog covering a new topic.

Thanks again for reading, till then bye and take care.