How do methods get their this binding?
You lose the receiver because the receiver comes from the call site. When you copy a method into a variable and then call that variable, the call looks like a plain function call with no receiver. So this has no value, and the method fails as soon as it tries to use it.
const timer = {
seconds: 10,
tick() {
this.seconds -= 1;
console.log('ticking, now', this.seconds);
},
};
const detached = timer.tick;
try {
detached();
} catch (err) {
console.log('detached:', err.name); // 'TypeError' in strict mode
}
// Fix 1: keep the call as a method call.
timer.tick(); // ticking, now 9
// Fix 2: bind the receiver once.
const bound = timer.tick.bind(timer);
bound(); // ticking, now 8
There are several fixes, and picking the right one is a sign of experience.
Call it as a method. This is the simplest fix, and it is the right one when the caller is under your control. Passing timer.tick as a bare function loses the receiver, but passing it as a callback and then calling that callback with the object keeps it.
Bind it. The bind method returns a new function with the receiver permanently attached to it. Use it when you are handing the method to something else that will call it later, such as an event listener or a timer callback.
Use an arrow function. An arrow function takes its this from the scope where it was written, so it works well for callbacks that you create inside a method. It does not work for the method itself, because then it would capture the wrong value.
Use the method shorthand. If you define the method with the shorthand syntax rather than as a function property, it still has a dynamic this. It behaves like a normal method when it is passed around, and you can bind it whenever you need to.