What exactly is "this" in Javascript? (Part 1)

What exactly is "this" in Javascript? (Part 1)

Prelude

This is a three part blog that will explain in detail how the reference keyword this behaves in different contexts of JavaScript. It's meant for beginners and/or developers who are trying to understand contexts in JS coming from different programming languages.

Definition of Terms

One of the most confusing words that I encountered while learning Javascript was the reference keyword this. I was coming from a pure object-oriented background, i.e Ruby and Java, in which the reference keyword normally just pointed to the enclosing object in which it was declared.

In Javascript:

this is a reference keyword that points to a certain variable or object based on the execution context (in-memory scope created when code is running) in which it is defined.

this in Javascript

Contexts(Scopes) in JS are a very vital part of the language. They help define access to values, variables and objects defined in a function, class or a block. Let's take a look at how this behaves in different contexts of Javascript.

The Global Context

The global context comprises of objects and variables that exist in the global scope. This means that all functions, classes or variables that are defined in the global context are available throughout your application. In web browsers, we have window and in Node.js we have global as the global object. All of these during execution create a global context. Let's look at an example below(Paste code in any browser console):

var gVariable = "foo";
gVariable == window.gVariable; // returns true
console.log(this); // ?

Question: What would be the output of the console.log(this)?

Answer: The window object.

Explanation: At this point, we are in the global context hence the keyword this will point to the global object in the browser. We can further confirm this fact by testing with this.gVariable on the console and it will return foo.

this.gVariable // returns foo

Note that we are using var simply because let and const are used to refer only to contexts bound by a block. A block in Javascript means any piece of code that is written within a set of curly braces for example a for loop, a function or even a class except in the case of an object literal. At the moment, we have no block, hence using const and let will not give you the same results.

const gVariable = "foo";
gVariable == window.gVariable; //returns undefined

The Function Context

In JS functions, this behaves a little bit differently based on where it is defined.

Question: What will be the output of invoking the following function in your browser?

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

Answer: The window object

Explanation: After the function invocation, testThis(), the value logged out on the browser console is the window object. The function invocation creates an execution context within the global context and hence because this has not been specifically assigned to refer to any object or variable, it defaults to pointing on to the window object. This debunks one of the common misconceptions that this will point to the function object that it is defined. Take note because it does not and you might run into some reference errors on your app. To explain it further, invoking testThis() after defining it is similar to writing it as follows:

window.testThis();

Here, we observe that before the invocation, we are referencing the window object to access the function testThis. As a matter of fact, when you expand the window object, you will find a function property with a key of testThis. Screen Shot 2019-11-27 at 12.43.09 PM.png After this observation, we can safely conclude that:

for all functions in JS, if this has not been specified to point to any object or variable, it will always result to referring to the global object by default.

Let's look at another example.

Question: What will be the output of the following code?

const student = {
  name: "John",
  sayHello(){
    console.log(`Hello ${this.name}!`)
  }
}
student.sayHello();

Answer: (drum rolls...) Hello John!

Explanation: When sayHello is invoked, it creates a context that exists in the student object because that's where it is called from. This means that this will be bound to the student object in which there exists the a property with the key name. Hence this is referencing the student object during execution of sayHello.

There's a pattern here!

What have you noticed from the two examples above?

We can not start to evaluate how this keyword works until we have a function invocation. During execution, this gets bound to an object and that's when we can clearly start to observe how it works. From the two examples, we get consistent reference for all the objects at the left of the dot.

window.testThis() // `this` references to window object

student.sayHello() // `this` references to student object

Therefore, it is safe to conclude that:

In the function context, this will point to the object at the left of the dot during invocation time.

The Class Context

JavaScript classes were introduced in ES6(ES2015) and behave much like functions hence determining the binding of this is a little bit easy following the above examples. The class keyword basically just provides what we call "syntactic sugar" but in real sense what's happening under the hood is basically dealing with functions. See this blog for more details.

Let's look at some examples that will help us understand better before we make a conclusion:

Question: What will be the output of the following code?

// Example 1
class Reference{
  constructor(){
    this.name = 'John';
  }

  sayHello(){
    const name = 'Peter';
    console.log(`Hello ${this.name}!!`);
  }
}

new Reference().sayHello();

Answer:(louder drum rolls...) Hello John!

Explanation: In the above example, we can see that this has been used in the constructor on the variable declaration name. However, sayHello function appears to have the same variable name but has no this keyword. Note also that because sayHello is a method inside Reference, we have to create a new instance of the class in order to invoke the method. This pattern is commonly used in many object oriented programming languages such as Java and Ruby. Once sayHello has been invoked, the variable const name is created in the execution context but when it comes to the invocation of the console.log() function, this reference keyword is bound to the class simply because as we saw in the above example, JS functions delegate the binding of this on the object at the left of the dot at invocation time. Now the enclosing object in this case is the instance created by the section new Reference() on the function invocation.

Let's look at another example:

Question: What would be the output of the following code?

// Example 2
class Reference{
  constructor(){
    this.name = 'John';
    console.log(this);
  }

  sayHello(name){
    this.name = 'Peter';
    console.log(`Hello ${this.name}!!`);
  }
}

new Reference().sayHello();

Answer:(even louder drum rolls...)

Reference { name: 'John' }
Hello Peter!!

Explanation: There's something interesting is happening here. We see two outputs because we have the first logging done in the constructor and the second one in the function sayHello. This happens because the constructor is automatically invoked once we have new Reference() invocation.

Let's look at how this behaves.

When we have the invocation of sayHello, the first binding of this keyword refers to the string "John" because of the constructor function invocation. The invocation of sayHello replaces that binding with the string "Peter" because the binding refers to the same object that is the class. Classes in JavaScript are a type of a function and hence instantiating new objects from classes behave the same as in functions which to an extend behave like objects as well.

With these two examples, we see the same level of consistency of this reference keyword referencing the object at the left of the dot. With the above, we can conclude that:

For any given class in JavaScript, this will always point to the class in which it is defined.

Conclusion

From the above examples, it's clear that the best determinant for the correct binding of this is the object at the left of the dot. It might be a function, class or an instance of a class.

In part 2 of this blog, we will get to see how concepts like function binding in JS affect this in both ES5 and ES6 as well. In part 3 we'll look at how this behaves in different JS frameworks.

I welcome any feedback and comments. Stay tuned ;)

References

Tyler McGinnnis

MDN API Docs

Javascript Info

Digital Ocean Blog

Image by Sydney Rae