A brief introduction to execution contexts and the value of the ‘this’ keyword in JavaScript

Learn how the value of the ‘this’ keyword is determined in JavaScript

Sergio Pichardo
8 min readJul 29, 2021

Topics

  • The Global Object
  • Execution Context (aka the value of this)
  • Implicit and Explicit Execution Contexts
  • Context Loss

Understanding how the value of the this keyword is determined is very important, otherwise, you might encounter some unexpected results when executing your programs. However, in order to understand how JavaScript determines the value of this, we must take a step back and take a look at another important concept before we begin, the global object.

The Global Object

When a JavaScript program runs, it creates an object that is accessible throughout your entire program called the global object. The global object in Node.js is the global object and when talking about JavaScript in the browser it's the window object.

Note: For the rest of this article, I’ll continue using Node.js as my programming environment.

The global object has the following characteristics:

  • It contains values, methods, and objects that are globally available throughout your program e.g. parseInt, Math, String, Number.
  • Variables declared without const, let, or var, and function declarations are added as properties to the global object.
  • It is used as the implicit execution context for function invocations.

Execution Contexts (Implicit and Explicit)

An execution context (or just context) is the environment a function executes in, which in JavaScript is also known as the value of the this keyword. There are two types of execution contexts implicit and explicit.

Implicit Execution Context

Implicit execution contexts are set automatically by JavaScript.

1) By itself, the value of this refers to the global object

As you can see comparing the value of this and the global object using the strict comparison operator evaluates to true because they are the same object. Also, you can explore the properties available to the global object using the Object.prototype.getOwnPropertyNames method.

2) Inside of an object, the value of this refers to the global object

When the code above is executed, it will print NaN to the console.

Surprised?

Because inside of an object the value of this refers to the global object when we use this.firstName and this.lastName to try to access their values we are not referring to the user object, we are referring to the global object, and in the global object the properties firstName and lastName don't exist, so they're set to undefined, then the expression becomes undefined + undefined, and since you can't perform mathematical operations on non-number values, NaN is returned.

So, how can we fix this problem?

The following is very important to remember:

JavaScript determines the value of the this keyword for function or method invocations based on HOW the function or method is invoked. Where or when the function or method is defined does not matter.

3) In a method invocation, the value of this refers to the object used to call the method

Now that we know this new rule, we know that when we refer to the username method, the value of this will refer to the user object.

So, with this new rule, we have a way to fix the problem in the previous code example, we can move the properties this.firstName and this.lastName inside of a function expression and return the computed value.

Also, notice that the user.username method was invoked as a method. This is why the value of this refers to the user object. What do you think would happen if we assigned the username method to a variable and we invoked it as a function?

4) In a function invocation, the value of this refers to the global object

In order to illustrate this, we’ll just make a minor change. We’ll assign the username method to a variable and then we'll invoke it to see what happens. Just as a quick aside, we can do this (assign a method to a variable) because functions are first-class values in JavaScript, they are just like any other value, so they can be assigned and passed around.

Because we’re now referring to the global object, we get the same problem we got earlier.

5) In a function invocation with strict-mode, the value of this is undefined

Because this does not reference theglobal object (it references undefined), attempting to invoke the getUsername function will trigger a TypeError: Cannot read property 'firstName' of undefined when we try to access the firstName property from undefined.

Explicit Execution Context

In JavaScript, explicit execution contexts are explicitly set by the developer. It refers to binding the value of the this keyword using the call, apply and bind methods. Let's go through each of the methods mentioned to learn how it works.

1) Explicitly setting the execution context of a function using the call method

The call method calls a function or method with a given execution context and also accepts optional arguments for the calling function/method.

Here’s the pattern for the call method:

calling a function with an explicit execution context using the call method:

You can use the call method to reuse code between constructors:

You can use the call method to invoke a function without specifying the execution context. When you use call without specifying the context, the context will be the global object.

2) Explicitly setting the execution context using the apply method

The apply method works exactly the same as call the only difference is the optional arguments are accepted as an array-like object.

Here’s the pattern for the apply method:

Using the apply method to explicitly set the execution context of a function

3) Using the bind keyword to "hard bind" the execution context of a function

The way the bind method works is different from the call and apply method. Thebind method returns a new function that is permanently bound to the object passed as the first argument to bind. Therefore, the original function or method is not affected. After you have "hard bound" a function to an object, you cannot "unbind" it, even if you call bindagain on that function or use the call or apply methods. Unlike, call and apply, the bind method is not immediately executed.

Example 1: Binding a function and assigning it to a variable

Notice that with bind now the username method will always reference the user object, and since it returns a new function we can assign it to another variable and call that variable later on in our code.

Example 2: A permanently bound function’s execution context cannot be changed

Even though we are calling the fetchData again with the call method using the smallData object, the execution context of the getData function does not change, because it has been permanently bound. So, what we'll get is two copies of the schittsCreekData.data printed to the console.

Context Loss

Sometimes functions can lose their execution context (aka the value of this) during specific scenarios, and when this happens, it is called context loss or it is referred to as a function losing its context. However, it's important to know that a function always has a context, and when we say it's lost, it simply that the context is not what we expect.

I’ll cover some of the most common context loss scenarios:

  1. When a method is extracted from an object and executed as a function
  2. When an inner function does not use its surrounding context
  3. When a function is passed an argument and loses its surrounding context

1) A function can lose its context when a method is copied (extracted) and invoked as a function

When we assign the sergiosTasks.getTasks method to the printTodaysTasks variable, the getTasks method's execution context is lost, and the value of this now references the global object. Why?

JavaScript determines the value of the this keyword for function or method invocations based on HOW the function or method is invoked. Where or when the function or method is defined does not matter.

When the getTasks method is assigned to a variable, and invoked as a function, its execution context is the global object., and since the global object does not have a tasks property, it returns undefined. So, when we attempt to access the forEach method from undefined a TypeError: Cannot read property 'forEach' of undefined is thrown.

Solution

To preserve the getTasks method's context, we can use the bind method and pass the sergiosTasks object as the execution context. This will permanently bind the getTasks method to the sergiosTasks object, and even if we pass its reference around, its context won't be lost.

2) A function can lose its context when an inner function does not use the surrounding context

Why is the context lost for calculateMonthlyWages function?

JavaScript determines the value of the this keyword for function or method invocations based on HOW the function or method is invoked. Where or when the function or method is defined does not matter.

I hope you’re starting to see a pattern here. Fortunately for us, there are a few solutions for this common problem, and we can preserve the execution context in the following ways:

  1. Preserve the context using a variable in the outer scope
  2. Call the inner function with explicit context using the call or apply methods
  3. Permanently bind the execution context using the bind method

Solution 1: Preserve the execution context using a variable in the outer scope

We can preserve the execution context by storing the value of this in a variable. Because JavaScript uses lexical scoping the code inside the calculateMonthlyWages function will have access to it, and the function will be able to return the information we need.

Solution 2: Call the inner function with explicit context (using call or apply)

Another solution could be using the call or apply methods to explicitly call the calculateMonthlyWages function with the correct context.

Solution 3: Permanently bind the execution context using the bind method

We can permanently bind the execution context of the calculateMonthlyWages function using a function expression and the bind method.

Solution 4: Using an arrow function

Finally, this might be the best-case scenario for an arrow function. Arrow functions inherit their execution context from their surrounding context. Therefore, the calculateMonthlyWages function will be able to preserve its context by getting it from the outer getSalaryInfo method.

3) A function can lose its surrounding context when a function is passed as an argument to another function

Another way we can lose the execution context of a function is by passing it as an argument into another function.

Solution

We could solve this problem in different ways. We could add another parameter to the logTasks function and pass a reference to the sergiosTasks object, then inside the logTasks function, we could use either call or apply to call the callback function using the reference to the sergiosTasks object as the execution context.

However, this problem can be solved in a more straightforward way using the bind method to permanently bind the getTasks method to the sergiosTasks object.

I know, that was a lot to digest, but guess what? There’s still more. This isn’t an exhaustive list of scenarios, but I hope you now have a better idea of how JavaScript determines execution contexts and how you can manipulate contexts using the call, apply and bind methods.

Key Takeaways

  • Global object: the global object is automatically created by JavaScript when a program runs. It is used as the implicit execution context of function invocations.
  • Execution context: The execution context refers to the value of the this keyword at any given point in time.
  • Implicit execution context: is automatically set by JavaScript.
  • Explicit execution context: is set by the developer using the call, apply, and bind methods.
  • How JavaScript determines the value of “this”: JavaScript determines the value of the this keyword for function or method invocations based on HOW the function or method is invoked. Where or when the function or method is defined does not matter.

Resources

  • Object-Oriented Programming with JavaScript — JS120 (Launch School)
  • call, apply and bind functions documentation (Mozilla Developer Network)
  • The this keyword (W3 Schools)

--

--

Sergio Pichardo

I’m a Software Engineer fascinated by the world of Cloud Computing and AI