Dude … we have to talk about this (in JavaScript)

Recently I stumbled about this post about The complete elimination and eradication of JavaScript’s this.

To give you a TL;DR: The author states that almost no JavaScript developer understands this, that it is a terrible concept, and introduces a library that allows developers to avoid using this in many circumstances. So, we had a twitter discussion with pretty opposing standpoints: I think this in JavaScript is quite simple, versatile and really useful.

But in the next days, it got me thinking …

Yeah, I’ve met my share of developers that I had to explain to how this works. But I don’t believe that introducing a new concept to avoid the use of this altogether is a good solution. It’s like avoiding olives all your life because you didn’t like them at the age of 13. When you are 55, you will try some by accident, they will be delicious and you will mourn the last 30 years of your life that could have been so much better with olives (or this, for that matter).

So, let’s actually talk about this

Before we go deeper, I’ll give you a simple rule of thumb:

this in a function is, in this order

  • what the function was explicitly bound to (using bind or a similar construct)
  • what the function was explicitly called with (using call or apply)
  • what was left of the dot at the moment of execution
  • something else like a global context that you should not rely on

„Left of the dot“? You’re trolling me right now!

Yeah, of course, that one rule seems the weirdest, but I think it’s the best way to put it and make it easy to understand. Let us start with two little test objects:

So, let’s run something on it:

Okay, we expected that. Now, what if we assign a.testFunction to b and call b.testFunction?

Although we initially defined testFunction on our object a, this when calling b.testFunction now is b.

So now you might get what I was meaning by ‚left of the dot‘. You could also say ‚called on‘, but I like to think about this as the 0th argument to a function. That way, it is very consistent with the signature of Function.prototype.call, which we’ll talk about later.

„Something else“? So it is all hogwash!

Yeah, no. Let’s continue with our example above and see what it does in different contexts, when there is nothing left of the dot:

When we execute this code in a browser, this points to the window object. Executed in node, we get the global object. In strict mode, it behaves a little differently:

Running it now in the browser or in node, we get the undefined value.

So yeah, it is predictable and defined. But my point is: if you want to access window or global respectively, just access window or global. Doing that access with this will help no-one understand your code. So, don’t do it and treat this in that context just as „something else“ and never think about it again.

But when I’m writing this.foo as a property in React/JSX, it does not set this to the value left of the point. What’s going on there?

Notice that above, I said ‚left of the dot at the moment of execution‘? What you are doing here is passing that function for it to be executed later. It will be stuffed into a variable, and just like in the last chapter where we assigned c = a.testFunction, the ‚left of the dot‘ context will be lost before it is finally executed.

This is why you need to bind functions you pass in jsx, or use the fat arrow notation.

Explicitly specifiying this at runtime: call and apply

You can also specify at runtime what this will be on method execution. Look at this example:

So while the function is stored in our object b, it is called on a. Alternatively, you cann use the apply method.

call and apply are pretty similar, but differ when you want to pass additional arguments to the function. The following two method calls would be identical:

Seeing the signature of call, you might also understand why I like to think of this as the „0th argument“ of a function.

Explicitly binding this: bind and fat arrow functions

So what if you have no control over how and when a method is called, but want complete control over what this is when it is executed? Then you can bind a method. Look at this example:

So even though you are calling testFunction on b, it was bound to a and so, this will always point to a.

Simplified, bind wraps your function in another function that always calls your function with the this that it was bound to.

While this is great, keep in mind that you cannot change what a function is bound to once it was bound once because you would only change what the wrapping function is bound to, not what the wrapped function is bound to. Because of the same reason, you cannot use apply or call to change this any more.

Fat Arrow Function

Now, let’s talk about a fat arrow function in that context. The following two statements are roughly equivalent:

So an arrow function automatically binds to the value of this at the exact moment the method is defined. This is called lexical binding or static binding.

That property makes it so useful in every situation where a function is passed as a value (in JSX for example), because this will be exactly what it is while you write it.

Phew. That was a lot of information. And you call that simple?

Yup. While there was a lot of information, just try to remember this even shorter version of my my rule of thumb from the top:

This is what was explicitly bound to, or what was specified at runtime, either by using call/apply or just by calling a function with something left of the dot.

Forget about that global/window stuff and stay clear of that usage. It makes your code less readable and my rule of thumb too complicated.

Für neue Blogupdates anmelden:


11 Gedanken zu “Dude … we have to talk about this (in JavaScript)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.