Avatar

(Over) Simplifying "this" in Javascript

02 Jul 2020

TLDR:

The this in a method is the object the method was called on

Introduction

Looking up “how does this work in javascript?” on google can return a large number of very good results. Most of them explain how this works in great detail, but I found that although the explanation is both correct and detailed, it usually involves going over multiple complicated scenarios and explaining a different “rule” for each scenario. All these special rules can quickly become overwhelming when you’re just trying to use this in a small part of your code.

So at the risk of brushing over the little nuances of this, I’ll try to (over)simplify all the different interwined rules into one simple rule, something that will hopefully be a bit easier to remember.

The Rule

The very over-simplified rule of this in JS is,

The this in a method is the object the method was called on

that’s about it.

An Example

Let’s jump into a simple example where we can see how this short rule can help us figure out what this means,

let obj = {
  add: function (a, b) {
    console.log(this); // 1st log
    console.log(a); // 2nd log
    console.log(b); // 3rd log
  }
}

What would this program output?

It may be tempting to say something along the lines of

“The first log will output obj , but it’s impossible to know the 2nd and 3rd log without looking at the call site”.

This answer isn’t completely wrong, but it sort of misses an important detail. The actual answer is that just like how it isn’t possible to know what the values of a and b before the method is called, it isn’t possible to know the value of this either.

The value of this has no relation to where the function or method is declared, just like the params a and b , and just like a and b it is set when the method is called not when it is created.

now if we changed it a bit to,

let obj = {
  add: function (a, b) {
    console.log(this); // 1st log
    console.log(a); // 2nd log
    console.log(b); // 3rd log
  }
}

obj.add(1, 2);

We can see that,

  1. The 1st log would output obj, since that’s the object the method is being called on,
  2. The 2nd log would output 1, since that’s the 1st parameter we passed
  3. The 3rd log would output 2, since that’s the 2nd parameter we passed

Another Example

That was a simple enough example, and something where the value of this seems pretty intuitive, specially if you used another language like Java or Python before.

Now, let’s look at another example, somewhere where the value of this might not be so intuitive

let student = {
  name: 'john',
  logThis: function() {
    console.log(this);
  }
}

let teacher = {
  name: 'alan'
};
teacher.logThis = student.logThis;

student.logThis(); // 1st log
teacher.logThis(); // 2nd log

What should the logs output?

  1. For the 1st log, .logThis(...) is called on the student object, so it outputs student
  2. For the 2nd log, .logThis(...) is called on the teacher object, so it output teacher.

The most common source of confusion seems to come from this line

teacher.logThis = student.logThis;

There is an expectation that student.logThis “belongs” to the student object, so even if we’re assigning it to another object, it should still “remember” where it came from. But the reality is that,

Functions do not “belong” to any object nor do they know which object they were created in. They are simply “called on” objects, and the value of this is the object they are called on.

So although objects may have have properties that point to functions, functions themselves have no idea which object they were created in.

Now that we have the most common case covered, let’s cover a few other cases

Case 1: What if it’s called without an object?

For example

function add(a, b) {
  console.log(this); // 1st log
  console.log(a); // 2nd log
  console.log(b); // 3rd log
}

add(1, 2);

The rule for this case is

The this in a method is the object the method was called on

i.e. the rules are the same.

A function in JS needs to be called on an object. If no object is specified, it’s called on a default object.

In node this value is an object called global .

In the browser it’s window.

In another runtime environment it could be something else. The important thing to remember is that all methods are called on objects, so even if you don’t specify the object to call the method on, the Runtime will run it against a default one.

So if we ran the above snippet in the browser

  1. The 1st log would be window, which is the default object in the browser
  2. The 2nd log would be 1
  3. The 3rd log would be 2

Case 2: What if it’s called with new ?

For example

function person() {
  console.log(this); // 1st log
}

let p = new person();

The rule for this case is

The this in a method is the object the method was called on

i.e. it’s still the same.

What new essentially does is create a “new object” i.e. {} and then call person() on the newly created object.

So running the above snippet,

  1. The 1st log would be {}, since person() is called on a new empty object.

Case 3: What if we use strict mode

For example,

'use strict'

function logThis() {
  console.log(this);
}

logThis();

The rule for this case is

The this in a method is the object the method was called on

i.e. it’s always the same.

The only difference is that in strict mode the default object will always be undefined i.e. it strict mode changes the value of the default object, but the rules for this remain the same.

Conclusion

A fun little thing to do is to replace the code in the snippets with ES6 arrow functions (() ⇒ {}) in the examples, and see if the outputs match (hint: it won’t). Which may make it seem like the rules for arrow functions are different. Fortunately, the basic rule is still the same, but the way in which arrow functions are called is a bit different. We can go over that in a future post.

Disclaimer:

I intentionally avoided using “execution context” and “invoking”, along with a few other terms. Although they’re the more accurate terms, they can seem confusing if you don’t already know what they are.

Further reading

“This or That” from You Don’t Know JS - Honestly, the entire book is amazing

MDN docs - A bit more technical, but a great resource nonetheless