The this keyword in JavaScript made simple.

Execution context and how the value of this is determined.

Eamon O'Callaghan
8 min readAug 9, 2021
You got this

The idea behind the this keyword is that when you invoke a method that contains the this keyword in its body, this points to an object and tells the method where to look to find the properties needed in the method. The value of this can also be referred to as the execution context of a function or method.

What you need to know:

  1. Outside of a function body, this points to the global object.
  2. Regular function calls use the global object as their execution context.
  3. Method calls use the calling object as their execution context.
  4. How this is bound depends on how a function is invoked rather than on where it’s defined.

Let’s examine each of those in more detail:

Outside of a function

The most important thing to know when determining the value of this in JavaScript is that if this appears outside of the body of a function or method, it points to the global object.

The global object is an object that always exists on the global scope or the top level of your code. Object, Array, Functionand other built-in constructors are all properties of the global object.

Inside of a function

When you invoke a regular function that contains the this keyword in its body, this points to the global object.

this points to the global object inside a function, but in strict mode it points to undefined.

Example

In the following code block the function printString will look for the properties subject and adjective. So based on what we have just learned, what will happen when we invoke the function printString below?

Why did this.subject and this.adjective evaluate to undefined? As printString was invoked as a standalone function, its execution context or this binding was implicitly set to the global object.

So printString looked at the global object, searched for a property named subject, and as there is no property named subject on the global object it evaluated to undefined. Then the same thing happened with this.adjective.

To prove that is what’s happening, we can add those two properties to the global object and see what happens.

Notice that on lines 5 and 6 we add the properties subject and adjective to the global object. Now when we invoke printString on line 8, it finds those properties and appends their values to the string.

Note: adding properties to the global object is not recommended. I’ve done it here to illustrate a point. Don’t do this in your code.

Also, note that the above code will not work in strict mode. Remember that in strict mode, this will point to undefined in a regular function call.

Summary:

  • If this appears outside of the body of a function, it points to the global object.
  • Regular function calls use the global object as their execution context.

Using this in methods

So far we have seen how this is bound in a standalone function but that’s not very useful. When this really comes into play is when we use it in object methods. Let’s add our printString function to an object and call it as a method.

In this example, the method printString looks for two properties, subject and adjective. How does it know where to look for them?

You may think that it just looks for those properties in the same object that the method appears in. That is not quite accurate though. Instead, it looks for the properties in the object that calls the method.

That is a very important distinction. What would happen if we stored object.printString in a variable and then called it?

Notice that we have stored object.printString in a variable called func and then invoked func on line 10. Now suddenly this is no longer pointing to object. Can you guess where it is pointing?

If you said the global object, you were correct. func is called as a standalone function on line 10 and not as a method, and as we learned earlier the implicit execution context of a function is the global object.

This teaches us something very important about the binding of this. The value of this is determined, not by where it appears in your code, but by how a function or method is invoked. Let’s see how we can get func working correctly again.

Summary:

  • Method calls use the calling object as their execution context.
  • You can’t tell the binding of this just by looking at the body of a function or method, you must look at how it is invoked.

Explicitly setting the context using call

So far we have mentioned several times how this is implicitly set when you invoke a function or method. That implies that we can also explicitly set the execution context.

All functions have access to a property method named call that they inherit from Function.prototype. We can invoke the call method on a function and explicitly set the execution context to whatever object we pass in as the first argument.

Notice that we invoke the func function on line 15 with the call method, and we pass in anotherObject as an argument. Now the execution context of func on line 15 is bound to the object anotherObject, and so it looks there for the properties subject and adjective.

Note that using call does not mutate func, so we can still call func as a standalone function and its execution context will still be implicitly bound to the global object. But what if we wanted to permanently bind anotherObject as the execution context of func?

Permanently binding an object

All functions also have access to a property method named bind that they inherit from Function.prototype. bind works differently from call in that it doesn’t invoke the function. Instead, it returns a new function with its execution context permanently bound to the passed in argument.

Notice how on line 15 we store the new function returned by bind to the variable newFunc.

When we invoke newFunc on line 16, we see that its execution context has been bound to anotherObject. It’s no longer possible to change the execution context of newFunc as it has been permanently bound to anotherObject. Note that func has not been mutated.

Summary:

  • We can explicitly set the execution context using call or bind.
  • call invokes the function with its execution context set to the passed in argument.
  • bind returns a new function with the execution context permanently bound to the passed in argument.

Why arrow functions are special

Now that you’ve taken all this time to learn how this is bound inside in functions, so you may be surprised to learn to arrow functions do not have their own this binding.

Notice that in the following code block, we’ve replaced our printString method with an arrow function.

When invoked, this.subject and this.adjective evaluate to undefined. This is because arrow functions do not have their own this binding, instead, they establish the binding of this from the surrounding code.

We know that when this is not in a function body, it points to the global object (or undefined in strict mode). Therefore, in this case, this points to the global object.

Once an arrow function is defined in your code, its execution context cannot be changed. If the arrow funtion is defined within the body of another function, it will always have the same context as the outer function.

Unlike with non-arrow functions, the execution context for arrow functions is determined lexically, which means that we determine the context from where the arrow function is defined rather than from how it is invoked.

As arrow functions do not get their own this binding, the methods call and bind will not work.

This may sound like a negative, but it is actually a very useful design feature as we will now see.

Dealing with context loss using Arrow functions

Let’s say we want to use an array method in our object within the printProps method. Notice on line 5 that we call forEach on anArray and we try to print out the elements of anotherArray.

Why did we get a TypeError? It turns out that when we pass a function as an argument to another function, we lose access to this in the inner function. This is called ‘context loss’. So when we pass an anonymous function as an argument to forEach on line 5, this on line 6 points to the global object rather than to object.

There are a few ways to fix this including using bind, but this is where arrow functions really shine! In the following example, we change our anonymous function to an arrow function on line 5:

Now it works! Why? Remember, arrow functions do not get their own this binding, instead they use this from the surrounding code. As the arrow function is in the body of printProps, it uses the this binding of printProps which we know to be object.

So while arrow functions are not suitable for use as methods, they can be very useful when we need a function within a function or when we need to pass a function as an argument to another function.

Summary

  • Execution context refers to the value of the this keyword.
  • Outside of a function body, this points to the global object.
  • Regular function calls use the global object as their execution context.
  • Method calls use the calling object as their execution context.
  • In strict mode, this is implicitly set to undefined rather than the global object within regular function calls.
  • Arrow functions do not have their own execution context, they use the context of their surrounding scope.
  • You can’t tell the binding of this just by looking at the body of a function or method, you must look at how it is invoked.

Thanks to Launch School where I studied all of this in far more detail. Feel free to check out some of my other articles on JavaScript and my journey through Launch School here.

--

--

Eamon O'Callaghan
Eamon O'Callaghan

Written by Eamon O'Callaghan

I’m a Software Engineer working at S1Seven. I mainly work with NestJS and TypeScript. I enjoy sharing what I’m learning by writing about it here!