this in JavaScript

Sep 19, 2024

JavaScript object is a powerful data structure that contains both data and methods. Consider the following example.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log("Hello from Kiran Chauhan");
  },
};

Here, person object contains firstName and lastName as data and greet() as method.

We use dot notation(.) or array notation([]) to access the data or call methods.

// Dot notation
person.firstName; // 'Kiran'
person.lastName;  // 'Chauhan'
person.greet();   // Hello from Kiran Chauhan

// Array notation
person["firstName"]; // 'Kiran'
person["lastName"];  // 'Chauhan'
person["greet"]();   // Hello from Kiran Chauhan

Most of the time, dot notation is enough to access data or call the methods unless we have more specific scenarios to work with.

Now, pay attention to the body of the greet() method.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log("Hello from Kiran Chauhan");
  },
};

Console message contains “Kiran” and “Chauhan” as hard-coded values. We can easily replace these hard-coded values with firstName and lastName. But, firstName and lastName are not variables. Instead, they are the properties of the person object. So that, we can’t directly access them like variables. Even if we try to do so, JavaScript will throw errors.

To use the properties of the object within object, we need to use this keyword. this means this object or object at the hand or self or etc. In other words, if we want to use properties of the object within object, then we need to write this.firstName instead of firstName and this.lastName instead of lastName.

const person = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function () {
    console.log(`Hello from ${this.firstName} ${this.lastName}`);
  },
};

The output remains the same. But, we removed the hard-coded values and replaced by the appropriate properties.

person.firstName; // 'Kiran'
person.lastName;  // 'Chauhan'
person.greet();   // Hello from Kiran Chauhan

In conclusion, this in greet() method refers to the current object or the object on the method is called upon(person). In simple words, whatever is on the left-side of the dot operator, this refers to it. In case of greet() method call, it is person object. So, this refers to the person.

---

But, consider the following example.

function magicSum() {
  const sum = this.a + this.b;
  console.log(sum);
}

We can simple call this function without dot as,

magicSum();

In this example, we do not have dot at all. Then the question is what this refers to in the magicSum() function? The answer is the global window object. When you define a normal function outside of anything, the function is attached to the window object and because of that you can call this function as magicSum() or window.magicSum(). Both calls are identical.

magicSum(); // NaN
window.magicSum(); // NaN

As a side note, when you define variables with var keyword, they are also attached to the global window object whereas let and const don’t.

var v1 = "a";
v1; // 'a'
window.v1; // 'a'

let v2 = "a";
v2; // 'a'
window.v2; // undefined

const v3 = "a";
v3; // 'a'
window.v3; // undefined

Continuing with the magicSum() function, this refers to the global window object. But, in the window object, we don’t have a and b. Due to this, we get NaN as a result. But, if we define variables a and b using the var keyword, then this.a and this.b resolve from the window object and we will get the current result as a sum than NaN.

var a = 40;
var b = 2;

Re-define this magicSum() function after these variables.

var a = 40;
var b = 2;

function magicSum() {
  const sum = this.a + this.b;
  console.log(sum);
}

Now, calling this function as magicSum() or this.magicSum() returns the correct result 42. this refers to the window object and resolve the values of this.a to 40 and this.b to 2 resulting 42 in magicSum() as sum.

The conclusion is, the value of this can be different based on how the function or method is called. If the function is called directly, this refers to the window object. But, if the method is called on the object, this refers to that object.

Things are not done here. We are halfway through. Based on the context, this can have three different sets of values and we can also set the value of this based on our requirements. So, this can have four different types of value based on context.


Do you know what a constructor function is? Consider the following example.

function Point() {
  this.x = 2;
  this.y = 4;
}

Note: Don’t worry about this in this example. We are going to cover it soon. That’s the main topic of this ongoing explanation.

This is the constructor function. Ok, this can be a constructor function. Ok, ok, this is not a constructor function. Actually, there is no unique syntax to define the constructor function. Instead, how the function is called, makes it a constructor function. When you use the new keyword to construct the copy of it, then it becomes the constructor function. When you make a copy of it using the new keyword, it will create an object. But, by convention, developers make the first letter capital to differentiate it from the normal function. Going back to the example,

function Point() {
  this.x = 2;
  this.y = 4;
}

We can create as many copies or the objects of this function we want using the new keyword.

const p1 = new Point();
const p2 = new Point();
const p3 = new Point();

Check the values of these p1, p2, and p3.

p1; // Point {x: 2, y: 4}
p2; // Point {x: 2, y: 4}
p3; // Point {x: 2, y: 4}

If you look further at the properties of the Point, you’ll find the Point() as constructor in console.

But, this is a useless function. Each copy has the same values of x and y. We can easily modify this function to accept the values of x and y.

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Now, we can create objects by passing the values of x and y.

const p1 = new Point(2, 2);
const p2 = new Point(3, 4);
const p3 = new Point(7, 3);

Now, these p1, p2, and p3 refer to the different objects with different values.

p1; // Point {x: 2, y: 2}
p2; // Point {x: 3, y: 4}
p3; // Point {x: 7, y: 3}

With this explanation, you know what the constructor function is, right? A function is usually defined with first-letter capital and you call it using the new keyword that creates a new object. But, what does it has to do with this? The answer is this plays an important role when you have the methods in it.

One more time, let’s define the same Point constructor function but with an add method.

function Point(x, y) {
  this.x = x;
  this.y = y;
  this.add = function () {
    console.log(this.x + this.y);
  };
}

Now, go ahead and create a p1 object.

const p1 = new Point(2, 2);

You can access the values x and y and the method add().

p1.x; // 2
p1.y; // 2
p1.add(); // 4

In p1.x, the left-side of the x is p1. So, this refers to the p1 object. That’s why you get 2. In p1.y, the left-side of the y is p1. So, this refers to the p1 object. That’s why you get 2. Finally, in p1.add(), the left-side of the add() method call is p1. That’s why this.x and this.y correctly resolve to x and y values of the p1 object and that’s why you get 4.

The conclusion is, if you’re using a constructor function, then this refers to the object that you created. Now, you know that based on the context, this can have three different values.

  1. Within an object, this refers to that object.
  2. In global scope, this refers to a window object.
  3. In case of the copy of the constructor function, this refers to the object that is created.

Finally, in JavaScript, we have flexibility to set the this to whatever object that we are interested in!


Consider the example, where I have the following two objects.

const person1 = {
  firstName: "Kiran",
  lastName: "Chauhan",
  greet: function (a, b) {
    console.log(a + b);
    console.log(`Hello from ${this.firstName} ${this.lastName}`);
  },
};
const person2 = {
  firstName: "Punit",
  lastName: "Tewani",
  bye: function (a, b) {
    console.log(a - b);
    console.log(`Goodbye from ${this.firstName} ${this.lastName}`);
  },
};

Notice, person1 has greet() method whereas person2 has bye() method. We can easily call these methods from respected objects.

person1.greet(2, 2); // 4 Hello from Kiran Chauhan
person2.bye(2, 2);   // 0 Goodbye from Punit Tewani

But, what I want to do is, I want to call the bye() method on person1 and greet() method on person2 as Kiran wants to say Goodbye and Punit wants to say Hello.

You can do it using call() or apply() methods. Both methods are almost the same except how they accept the arguments is different in terms of syntax.

The first argument to the call() method is this that you want to make as an object and remaining arguments are the arguments that the method that you’re calling needs separated by commas which are a and b.

call() method is called. In other words, you need to chain this method on the method that you’re interested in calling. In our case, the method is, greet(). So, we need to write,

person1.greet.call(); // This is partial code. Won't run.

Yup, we don’t have to write () after greet as it is call()’s responsibility to call this greet() method.

As mentioned previously, The first argument to call() method is the object that you want to make this which is in our case, person2 as Punit wants to say Hello even though he doesn’t have greet() method.

person1.greet.call(person2, 2, 2); // 4 Hello from Punit Tewani

this.firstName and this.lastName within the greet method are resolved against the values from person2 object using the call method.

If you use, apply() method instead of a call, you’ll get the same result. But, instead of passing arguments as comma separated values, we need to pass within a single array.

person1.greet.apply(person2, [2, 2]); // 0 Hello from Punit Tewani

The difference between call() and apply() is how you pass the argument to the method greet(). In case, if you have a method that doesn’t accept any argument. Then both call() and apply() methods serve the same purpose.

Because of the call() and apply() methods, Kiran can say Goodbye even though he doesn’t have bye() method.

// You can read the following as _call person2's bye method on person1_.
person2.bye.call(person1, 2, 2); // 0 Goodbye from Kiran Chauhan

// You can read the following as _apply person1 object in person2's bye method_.
person2.bye.apply(person1, [2, 2]); // 0 Goodbye from Kiran Chauhan

Let’s define the following callTwice() function.

function callTwice(cb) {
  cb();
  cb();
}

This function accepts the callback and calls the callback twice. If we do,

Note: For a moment, I’m not worried about passing values of a and b.

callTwice(person1.greet); // NaN Hello from undefined undefined x2
callTwice(person2.bye); // NaN Goodbye from undefined undefined x2

Even though person1 and person2 are clearly mentioned for greet and bye method, this.firstName and this.lastName are not correctly resolved. Things are tricky in callbacks. To resolve this, we need to use the .bind() method. This .bind() method correctly binds the scope of this and returns a new method. This method accepts an object that you want to makethis. In the case of person1.greet we need to call the bind method and pass person1 as an object to correctly bind the person1 object. Same is true for person2.

const greet = person1.greet.bind(person1);
const bye = person2.bye.bind(person2);

Now, call the callTwice function as we did previously.

callTwice(greet); // NaN Hello from Kiran Chauhan x2
callTwice(bye); // NaN Goodbye from Punit Tewani x2
Tags: javascript