Clarifying This in JavaScript

William Mills
Launch School
Published in
4 min readAug 21, 2019

In JavaScript, this is an integral concept that is easily stumbled over. this refers to the the execution context of a function, which gets established at execution time, not definition time.

It’s important to understand the difference, because JavaScript has first-class functions — which means that functions are treated like regular objects in the sense that they can be stored in variables and passed around to other functions.

Functions that take other functions either as arguments, return a function or both are known as higher order functions.

So why do we care that this gets established at execution time? Because if we take a function and pass it around without being careful, we will experience context loss because the value of this will change based on where that function finally ends up being invoked. Clear as mud, right? Let’s look at an example:

Losing context

On line 3 we assign an anonymous function to the property getBar on the object foo. When we invoke getBar in this context on line 8, getBar is a method of the object foo, so the implicit value of this on line 4 is the parent object foo, and the code produces an unsurprising result: logging the value of foo.bar which is baz.

Now for the trickiness: line 10 assigns the value of foo.bar to the variable qux in the global context, and does so without invoking the anonymous function stored within foo.bar. When we do invoke the function stored in qux on line 11, the execution context is now different than it was before, so the implicit execution context here is the global object. In other words, this now refers to global or window (depending on whether this JavaScript is running in node or in a browser — from here on I will just say global).

So why does line 11 log undefined ? Because we experienced context loss! We tried to log the value of this.bar, except that in this execution context this.bar resolves to global.bar. Because there is currently no bar property on the global object, the returned value is undefined.

So how to we fix this?

Instead of relying on the implicit execution context, we can decide for ourselves what the execution context should be (i.e. value of this within the invoked function). We call that explicit execution context. There are several ways we can achieve that.

Explicitly setting context

Here, at invocation time we utilize the the Function.prototype methods call and apply to explicitly set this to the object foo when the function executes. It’s a trivial example, but worth noting that we could pass any context here, not just foo. While call and apply may look to be aliases for one another, they are not. Both methods take the explicit context to be set for their first argument. The difference is that apply expects its next argument to be an array containing all the other arguments to pass on to the function, while call expects each argument to be passed individually.

Let’s examine those differences and their limitations before we look at a more permanent solution.

Clarifying the difference between call and apply

Here we can see that apply expects an array of arguments that it will pass on to the function that is having its context set, while call needs each argument to be passed separately. Line 12 logs NaN (or ‘Not a Number’) because 5 * undefined === NaN. It’s worth noting that when we use both call and apply, we are setting the context of this only for that specific function execution. For a more permanent solution we need to use bind.

Using bind, and demonstrating the thisArg for forEach

By permanently binding the context of qux on line 10, each invocation of qux will have its context bound to foo and will no longer experience context loss. Now take a look at line 6. By passing a second argument to forEach we can utilize the optional thisArg argument to similarly set the context of the callback function passed to forEach. It’s a clunky technique, but I’ll show a better way shortly. In the above example, because the callback function passed to forEach on line 4 is not a method on the foo object, it will implicitly execute as a function, not a method. By executing as a function, its implicit value of this would resolve to the global object, except that we corrected for that by passing in the thisArg.

This kind of messiness is why I prefer to use ES6 arrow functions to achieve the same thing with a much cleaner syntax. ES6 arrow functions do not change the context of this. Also note that as a one-liner, the braces can be omitted. The parenthesis can also be omitted because there is only a single function parameter.

Same functionality, but using an ES6 Arrow Function

Keep in mind when using bind that it will only work with a function expression (i.e. a function stored in a variable). It will not work with a function declaration. There are other ways of passing the context of this into functions nested in methods, such as saving the value of this into a local variable called something like self, but that approach is messy and made irrelevant by ES6 arrow functions.

Summary:

It’s imperative to remember that function execution context a.k.a this is implicitly determined at invocation time, unless it gets explicitly set beforehand. Function execution context (this) can be explicitly set for a specific invocation with call or apply, or permanently bound by using the bind function.

For further reading, check out the MDN docs: call, apply and bind.

--

--