DEV Community

rahulrao0209
rahulrao0209

Posted on

Understanding this in JavaScript

The Topics covered are as follows

  • this keyword
  • implicit binding
  • window binding
  • explicit binding(bind, call and apply methods)
  • new binding
  • this in arrow functions

Why is this used ?

This keyword is used to set the context with which a function is called in javascript. Say that this keyword decides what we should focus on when calling a function.
Note that the value of this keyword can always change depending on the circumstance and is not known until a function gets called, similar to how we don't know what the values of function parameters are until we call the function.

Lets understand via an example

In the below example we see that the method inside of the object named India is using the this keyword to refer to the
properties of the object inside which the method is defined. In this case the context with which the method is called is the object India. We can understand that by looking at what lies at the left of the '.' when calling a method. This kind of binding is referred to as implicit binding wherein this keyword refers to the object that is to the left of the '.' when calling the method.

const India = {
    name: "India",
    population: "1.2 billion",
    located: "South east Asia",

    info: function() {
       console.log(`${this.name} is a country with a population of 
       ${this.population} and is located in ${this.located}`);
    }
}

India.info();
Enter fullscreen mode Exit fullscreen mode

What happens if we take the method out of the object and use it as a function instead?
What do you think will happen?

 const info = function () {
    console.log(`${this.name} is a country with a population of 
    ${this.population} and is located in ${this.located}`);
}
info();
Enter fullscreen mode Exit fullscreen mode

As you will see, the console outputs

  • undefined is a country with a population of undefined and is located in undefined.

Now we get the above output due to the fact that the context (think object) with whose reference the function is getting called does not have the necessary variables defined. Namely outside of any object, the this keyword would point to the global window object in case of the browser and the globalThis object in case you're using node.js .

We can confirm this by typing this in the browser console and we'll see something as follows:

Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}
Enter fullscreen mode Exit fullscreen mode

Now since you know that the this is referencing the global window object let's try putting our required variables inside the
window object


window.name = "France",
window.population = "60.7 million",
window.located = "Western Europe"

info()
Enter fullscreen mode Exit fullscreen mode

Now you will see that, the console outputs

  • France is a country with a population of 60.7 million and is located in Western Europe.

We're able to access the values for our variables since we have added those values to the global object or the global context.
So what does this behaviour imply? It simply tells us that when we do not explicitly specify a context(think object of focus), the this keyword will point to or refer the global object. This object is called globalThis for node js runtime and is referred to as window object in the browser. This behaviour is what we call as window binding.

NOTE : Strict mode does not allow us to do window binding and attempting the same would throw an error as follows

 Uncaught TypeError: Cannot read properties of undefined 
Enter fullscreen mode Exit fullscreen mode

This happens because in strict mode the value of this when not set does not default to the global object and is instead set to undefined.

How to change the context as per our requirement?

Explicit binding with call, apply and bind methods.

You most likely want to extract the method out as a function and just change the context as per your requirement.
How do you do this? Well, every function in javascript provides three methods to do this namely - call, apply and bind. You can understand these methods as allowing you to call a function with a different context, apply a different context to a function, or bind a function to a different context.
Lets make use of each of them and see some differences in their usage.

const Canada = {
    name: "Canada",
    population: "38 million",
    located: "North America"
}

const USA = {
    name: "USA",
    population: "302 million",
    located: "North America"
}
Enter fullscreen mode Exit fullscreen mode

Above we have defined two objects named Canada and USA.
Lets call our info function with the context of Canada object.

info.call(Canada);
Enter fullscreen mode Exit fullscreen mode

The above invocation of the call method will print the follwing to the console

  • Canada is a country with a population of 38 million and is located in North America.

We can also specify any additional arguments along with the context
using the below syntax.

info.call(ContextObject, arg1, arg2, arg3); 
Enter fullscreen mode Exit fullscreen mode

apply is exactly similar to call and only differs in the fact that you must pass the additional arguments as an array.

info.apply(ContextObject, [arg1, arg2, arg3]) 
Enter fullscreen mode Exit fullscreen mode

bind method is very similar to call and apply in terms of its functionality with the difference being that unlike call and apply, bind does not call the method immediately. bind returns you a new function which is bound to a context or bound to the object with which it is called.

Let's see an example of bounded function below

const boundedFunc = info.bind(USA);
boundedFunc();
Enter fullscreen mode Exit fullscreen mode

In the above example we're binding the info function to the USA object and storing it in a variable named boundedFunc. If we call the boundedFunc, the output will be as
follows

  • USA is a country with a population of 302 million and is located in North America.

New binding

New binding takes place when we use the new keyword to create new objects using a constructor function. Let's quickly take a look at an example to better understand the process.

const Country = function(name, population, location) {
  this.name = name;
  this.population = population;
  this.location = location;
}

const switzerland = new Country("Switzerland", "8.6 million", "Western Europe");
Enter fullscreen mode Exit fullscreen mode

When we call the above constructor function using the new keyword, the function creates a brand new object for us, adds the specified properties and returns the object. This process is called new binding wherein this keyword inside the constructor function points to the object created by the function.

this in Arrow functions

Arrow functions do not have a this keyword of their own and the value of this in arrow function depends on what the value of this was in the lexical context of the function when it was created.

Let's take an example by typing the following in the browser console

const Poland = {
    name: "Poland",
    population: "30.8 million",
    located: "Eastern europe",



    infoArrow: () => {
      console.log("this: ", this);
    },

    info: function() {
        return (() => this);

    }
}
Enter fullscreen mode Exit fullscreen mode

Here in the infoArrow function the this keyword will point to the window object because as we said in arrow functions the value of this keyword depends on the lexical context in which the function was created.

Calling the infoArrow function will print the following
in the console

this:  Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}
Enter fullscreen mode Exit fullscreen mode

Now let's try with our normal function. The value of this here can be decided in the normal way as explained previously. Our normal function returns an arrow function which in turn returns the value of this. Let's see how the value of this returned by the arrow function will differ based on the lexical context.

Case 1

Let's call the method info() and store the return value in a variable ArrowFn. This variable contains the arrow function (() => this). Note that while the arrow
function get's created, the value of this in its lexical context is the Poland object and hence if we run this arrow function we will get the value of this as the Poland object.

let ArrowFn = Poland.info();
console.log("Value of this: ", ArrowFn());
Enter fullscreen mode Exit fullscreen mode

Above code outputs the following:

Value of this:  {name: 'Poland', population: '30.8 million', located: 'Eastern europe', infoArrow: ƒ, info: ƒ}
Enter fullscreen mode Exit fullscreen mode

Case 2

Let's now just store a reference to our info method in a variable fn. Now fn is variable which consists our info() method. Note that we're effectively taking our info() method out of the Poland object and storing it in a variable fn as a function.

let fn = Poland.info;
Enter fullscreen mode Exit fullscreen mode

Now let's try calling fn() and store its return value(which is the arrow function) in a variable Arrowfn2. Now calling the arrow function will return the global window object because when the arrow function was created(by calling fn) due to window binding, the this will now reference the window object instead of the Poland object.

let ArrowFn2 = fn();
console.log("value of this: ", ArrowFn2());

Console o/p
value of this:  Window {0: global, window: Window, self: Window, document: document, name: '', location: Location, …}
Enter fullscreen mode Exit fullscreen mode

Note - We cannot explicitly set the context of the arrow function using bind, call and apply as our first argument which sets the context/object will be ignored by the arrow function and by convention must therefore be set to null when being used with arrow functions. Another consequence of these behaviors of the arrow functions is that we cannot use it as a constructor function.

And that's what the this is all about in javascript ! :)

Top comments (0)