How 'this' works in JavaScript?

There is a lot of confusion around the this keyword of JavaScript in Js World mostly for new comers its a mystery because function's this behaves differently than other languages like java, C#.

When you are working with JavaScript this is quite common thing you come across but very few developers take a moment to understand it i.e How it actually behaves and how its value is calculated.

How value of this is determined?

Basically the value of this is basically depends on how the call to the function is being made. Now the question is how many ways the function call can be made in JavaScript? So lets talk about it.

There are four ways the function can be call in JavaScript.

  1. Function Invocation
  2. Constructor Invocation
  3. Method Invocation
  4. Indirect Invocation

Now we will see in each case the how the value of this is calculated

1. Function invocation :-

Let's start by working in fresh global environment , we have a function like


function foo(name) 
{ 
  this.name = name;
  console.log('value of this : ', this); 
}

function invocation pattern is the most common pattern of calling the function by simply putting () after function name.

now calling above function like.

// function invocation pattern

foo('fip');

// output

// value of this: Window {0: global, 1: global, 2: global, window: Window, self: Window, document: document, name: "fip", location: Location, …}

from above code snippet we can see the value of this inside function invocation pattern is Window i.e. global object and the property name is created in global object.

2. Constructor Invocation:-

In constructor invocation type of function calling , the call to the function is being made through new keyword. In this case the value of the this is the newly created object using function. Lets go through by example :

function Car(brand, model, year) {
  this.brand= brand;
  this.model = model;
  this.year = year;

  console.log('value of this : ', this);
}

// constructor invocation
var nexon = new Car('Tata', 'Nexon', 2016);

//output 

 // value of this :  Car {brand: "Tata", model: "Nexon", year: 2016}

from above snippet the Car function logs the newly created Car object instance as this.

So what new keyword does under the hood

  • Create a fresh blank JavaScript Object.
  • Adds a __proto__ property to the object which link to the which links to the function's prototype object i.e in this case Car.prototype. You can verify this by comparing .
 nexon.__proto__ === Car.prototype;

// output 
// true
  • Then bind the newly created object instance to this.
  • Return the newly created object from the function. in this case function ignores the return statement if any and return newly created object i.e this.

3. Method Invocation

Method invocation refers to the invoke a function that resides in a JavaScript object. In this type of invocation the value of this refers to the object instance on which the function call is being made.

Lets see though example :

const car = {
brand :  'Tata',
model :  'nexon',
year :   '2016',
getModel : function () {
             console.log('value of this: ', this)
             return this.model;
          }
};

// method invocation
car.getModel();

// output 
// value of this:  {brand: "Tata", model: "nexon", year: "2016", getModel: ƒ}
// "nexon"

In getModel() function value of this is the object which invoking the method i.e. car in this case.

4. Indirect Invocation

In JavaScript we can call the function either with() or call() & apply() methods. if we use call or apply to invoke a function , it takes first argument as a object which would be considered as a this inside the calling function. In this case we need to explicitly pass the value of this to the function hence this pattern knows as indirect invocation.

function getModel() {
 console.log('value of this: ', this)
 return this.model;
}

const car = {
brand : 'Tata',
model : 'nexon',
year : '2016'};

// indirect invocation
getModel.call(car); 
getModel.apply(car);

// output

// value of this:  {brand: "Tata", model: "nexon", year: "2016"}
// "nexon"

here in above snippet we can clearly see that supplied object considered as this.

Till now we have seen four ways how value of this is determined , but there is one exception which works differently than earlier ones which is - Arrow Function

Arrow function is special kind of function expression which does not have its own this binding and arguments object and cannot be used with new operator.

An Arrow function uses a value of this from enclosing execution context i.e from where it was created.

To understand this lets see some example :

function foo() {
console.log('value of this :' , this);
}

const koo = () => console.log('value of this :' , this);

 foo();

//output
//value of this : Window {0: global, 1: global, 2: global, window: Window, self: Window, document: document, location: Location, …}

koo();

//output
//value of this : Window {0: global, 1: global, 2: global, window: Window, self: Window, document: document,  location: Location, …}]

In both case output will be window object . How come ? Here output is same but reasons for output is not. Remember ! Arrow function does not have its own this , it get its value from enclosing context which is window object in this case.

let see another example :

const car = {
brand : 'Tata',
model : 'nexon',
year : '2016',
getModel : function () { 
              console.log('value of this : ', this);
              const doit = () =>
                { 
                 console.log('model : ', this.model);
                };
            doit();
    }
};

car.getModel ();

//output 

// value of this :  {brand: "Tata", model: "nexon", year: "2016", getColor: ƒ}
// model :  nexon

In above car.getModel () which in method invocation the value of this , we just discussed it above it would be invoking object. i.e. car; The doit arrow function which resides in getModel refers/borrows the this from its enclosing context which is this of getModel in this case thats why we can see the result of doit() was refered from car object.

So where the Arrow function would be useful then , one of the useful scenario for arrow functions are callbacks. As from callback we know , callback are executed somewhere in future , so in that case Arrow functions remembers its enclosing context/ borrowed this.

Thank you for reading the post. Do drop your feedback in the comments below and if you liked it, share it with your developer friends who might find it useful too.