JavaScript Wizard: What’s up with ‘this’?

JavaScript Wizard: What’s up with ‘this’?

JavaScript Wizard: What’s up with ‘this’?

The infamous this keyword — one of the things with JavaScript that seems to confuse developers more than anything else. More often than not, this seems both unpredictable and illogical, and it certainly does not act like in most other programming languages. If you’ve ever been pulling your hair because of this, you’re definitely not alone.

Can you figure out what gets printed from the snippet above? Here’s a spoiler: Hi, I'm undefined, and I'm a undefined Probably not what was intended from the looks of the code. Why does this happen? Because this got lost somewhere along the way.

So what’s up with this ?

In this article, I’ll explain how this gets bound, and how to ask yourself 4 questions from which you can always determine what this points to, in any given context.

Lexical scoping vs. dynamic scoping

First, we need to understand how scoping works in JavaScript. As with most programming languages, JavaScript uses lexical scoping.

This means that a variable or function may only be referenced from within the scope in which it is declared. Scopes are strictly nested within each other, which also means that a variable or function can be referenced from an inner scope. Most importantly, the scope is determined at compile time, hence the structure of the scope does not change during executing. The outermost scope in JavaScript is known as the global scope and has a reference to the window object.

With dynamic scoping, the scope from which we can reference declared variables or function changes dynamically.

Let’s try to do an example with this pseudo-JavaScript, and pretend that it now uses dynamic scoping

From dynamicBuilding() we’re trying to reference a variable, that is neither declared in its own scope or the scope that wraps it; the global scope. When we just call this function, this will return undefined or throw a reference error. But if we declare the variable in the same scope from within which we invoke the function, suddenly we don’t get a reference error anymore. The key takeaway here is, that the dynamicBuilding() behaves differently depending on where it gets called from. The scope — or execution context — from where a function is invoked, is called the call site of the function.

NB. The above example is pseudo-JavaScript. It is not how JavaScript works, and will not execute as in the example.

4 ways that ‘this’ is bound

At this point, you may wonder why I’m talking about scoping in JavaScript when this article is about the this keyword. The reason is, that the principle of lexical scoping in JavaScript — which most of us are used to, and find perfectly natural — is the main reason that we are thrown off when it comes to this.

JavaScript does not use dynamic scoping, and it never has, but in regards to the way JavaScript does this-binding, we actually get pretty close to something that reminds thereof.

So here it comes: With JavaScript, this is determined by the call site of the function of which this is referenced. That is, whenthis is used within a function, this will be a reference to the execution context from which the function is executed.

There are 4 different ways that this gets bound in JavaScript, and they all behave differently:

  • The new keyword

  • Explicit binding

  • Implicit binding

  • Default binding

That also means, that these are the 4 key questions that you will be asking yourself to determine what this points to, in any given context. Let’s go through these, one by one.

The ‘new’ keyword

When using this in a function, the first thing you need to ask yourself is if you used the new keyword when calling the function.

When applying the new keyword, an instance of a user-defined object will be created, and this will be bound to this object. The new keyword will do the following 4 things:

  1. Create a blank, plain JavaScript object.

  2. Link this object to another object.

  3. Pass the newly created object from Step 1 as the this context.

  4. Return this if the function doesn't return its own object.

As we see in the example above, this will be a reference to the object that will be created from calling the thisGuard() function with the new keyword.

In ES6, classes were introduced. To clarify: In JavaScript, there are no such things as classes — this is nothing but syntactical sugar that does exactly what we see above. Hence, the way this is bound works exactly the same with classes. The following is the equivalent of the code snippet above:

Explicit binding

If it is not the case that you applied the new keyword, the next thing to ask yourself is if you have used explicit binding. There are several ways you can call a function, and explicitly define what this should be a reference to when used within that function.

In the above example, we’re using explicit binding by calling the thisGuard() function, thus using the apply() method, that belongs to the Function.prototype.

What this does, is that it’s calling the function and passing in an object as a parameter. From within the function that is called, this will be bound to the object that is passed as a parameter. This is an effective way to ensure that this gets predictably bound to the object we desire.

A very similar method call() results in the same behavior. The main difference between apply() and call() is, that if we want to pass in additional parameters to the function, we can do this in two different ways: With apply() the second parameter would be a list of additional parameters, and with call() the additional parameters are listed as we usually would.

Another case of explicit binding is by using the method bind(), which was introduced in ES5. We will get back to an example of that in a little.

Implicit binding

If you didn’t use the new keyword when invoking the function, nor applied explicit binding, the next thing use should look into is if this is implicitly bound.

Implicit binding occurs when the function from where this is referenced, is a method of an object.

Let’s revisit the very first example from this article, yet without the setTimeout()

In this case, identify() is a method of the thisGuard object, and this will be implicitly bound to that object.

It’s worth noticing how explicit binding takes precedence over implicit binding, and how hard-binding takes precedence over explicit binding.

Default binding

Finally, if none of the above cases are present, you can expect this to bind to the global scope by default. That is, you can expect a reference to the window object, when referencing this. If you are using strict mode, you can expect this to be undefined.

If we revisit the very first example from this article as it was originally, you might be able to see why it prints undefined instead of the intended firstName and rank

When we’re passing the function as an argument to setTimeout, we’re not invoking the function, instead, we’re passing a reference. After one second, the function will be invoked as a callback function and the execution context will be the global scope, hence this will not be implicitly bound to thisGuard as we would expect.

With the knowledge about how this works in JavaScript, we know that we can easily fix this problem by hard-binding this to the thisGuard object.

Arrow function expressions

Lastly, I want to mention the use of arrow function expressions. In ES6, the arrow function was introduced. One of the main advantages of using arrow function expressions as oppose to regular function expressions is that this, from within an arrow function, will always point to the enclosing lexical context.

Let’s see an example with regular function expressions

We see here how the first call to regularFunction() uses default binding and the second call uses implicit binding. The result is that this points to two different contexts depending on the call site, just as we would expect.

If we wanted to make sure, that this would be lexically bound, rather than dynamically bound, we could instead use an arrow function.

Now the call site of the function no longer have any influence on what this is going to reference when used within arrowFunction().

Conclusion

The main reason that the value of this is confusing us, is that references in JavaScript normally follow the lexical scoping model, whereas this is a reference to the execution context from which the function is called. Thereby this is dynamically determined and breaks with the general conception of references in JavaScript.

However, we can always determine what this points to, by going through the 4 ways this can be bound. Alternatively, we can use arrow functions to make sure, that this won’t get bound dynamically.

For more know-how about JavaScript, I suggest you also browse through my other article, *JavaScript Wizard: Tips & Tricks*

That’s it! If you have any questions or feedback, please feel free to comment below. If you liked this article, give the clap 👏 button a couple of hits! You can also find me on Twitter, where I’ll be posting more content similar to this.