This post (This binding in JavaScript – 6. Gotchas and final notes) was originally published on Sargalias.
In this series we talk about this
binding in JavaScript.
This is a very important topic. It's also something that even experienced developers frequently get wrong and / or have to think about.
Basically in JavaScript there are 4 modes for this
binding. Make that 5 if we include arrow functions.
In order of lowest priority to highest priority, here they are:
- Default binding
- Implicit binding
- Explicit binding
- New binding
- Arrow functions
- Gotchas and final notes
In this post we'll talk about some gotchas and final notes.
Gotchas
If you understand the rules we already spoke about regarding this
binding, then you'll be fine. However there are some easy to miss cases with this
binding that are probably worth mentioning.
Gotcha - Synchronous function references
This is the case we already examined in the default binding section.
Basically if we pass a function reference to a variable, and then call the function plain, without a "." before it, we lose our binding to this
.
For example:
'use strict';
const obj = {
foo() {
console.log(this);
},
};
const foo = obj.foo;
foo(); // logs out undefined
Remember that we are calling the function plain, which results in default binding.
The thing that may be confusing is that we're passing the function reference obj.foo
. However note that we're not actually calling it, we're not using ()
. Instead we call it later with foo()
.
Gotcha - Function references in timeouts
Another tricky case is if we pass function references to things like setTimeout
. When we do this we also lose our binding to this
.
Here is an example:
const obj = {
foo() {
console.log(this);
},
};
setTimeout(obj.foo, 1000);
What does the code above log to the console?
Answer: undefined
This is surprising at first.
However it's really the same thing as the previous example.
We're passing obj.foo
to setTimeout
, so we might understandably think that implicit binding will occur and this
will be bound to obj
.
However that's not the case.
The important thing to realise is that we're not calling the function right then and there with obj.foo()
.
Instead we're just pointing to a function. The location of that function is obj.foo
.
In the meantime, we can imagine that setTimeout
has this kind of definition.
// Example setTimeout definition
function setTimeout(fn, ms) {
// Wait ms miliseconds
fn();
}
Note that inside our mock setTimeout
implementation, we're calling the function plain.
Obviously the code for setTimeout
is far more complicated, but the basic principle stays the same.
We are passing a function reference to setTimeout
, we are not calling the function directly with the correct context.
The same applies to anything in JavaScript where we pass a function reference.
Further reading
If you want to learn more about this
binding, the best resource I know of is You Don't Know JS: this & Object Prototypes by Kyle Simpson. You can buy it at eBooks.com or read it for free on GitHub
My thoughts on binding in JavaScript
This section is just my thoughts / rant on this
binding in JavaScript.
In short, I don't like it.
The reason is because it goes against common programming principles such as the principle of least astonishment and abstraction.
Basically when we program, we do not want to be thinking about implementation details for handling this
binding correctly. We're thinking at a level above those details, how to make our program work, not on how to handle the details of this
.
Additionally it's a feature that I've never used positively in any context. It has always been a feature which has gotten in my way, never a feature that has helped out.
I mean, sure, it's an implementation detail that's probably required for us to have dynamic classes. However I think things would be much easier for developers if the rest of the this
binding details were hidden from us.
Anyway, to combat this, I always default to using arrow functions, especially whenever this
is involved and I don't want to deal with unexpected behaviour.
And... That's all. I hope you enjoyed this series.
Top comments (0)