how 'this' behaves in the call, bind, apply

For about two years I wrote .bind(this) the way some people wear a lucky shirt. I didn't know what it did. I knew that when my code broke, adding it usually fixed things. That's not engineering, that's superstition.
So one evening I sat down and actually worked out what call, apply and bind are for. Turns out it's much smaller than I'd built it up to be.
The one sentence version
All three do the same job: they let you decide what this will be inside a function. They only differ in when and how you hand over the arguments.
function intro(city, job) {
console.log(`${this.name} from ${city}, works as ${job}`);
}
const me = { name: 'Atiq' };
intro.call(me, 'Dhaka', 'engineer'); // runs now, args listed out
intro.apply(me, ['Dhaka', 'engineer']); // runs now, args in an array
const fixed = intro.bind(me); // runs later, returns a new function
fixed('Dhaka', 'engineer');That's the whole thing. call and apply fire immediately. bind doesn't fire anything, it hands you back a copy of the function with this welded on.
The way I remember which is which: apply takes an array. Silly, but it stuck.
So when do I actually reach for each one?
call — when I'm borrowing a method from somewhere else. Array methods on things that aren't arrays is the classic case.
const args = { 0: 'a', 1: 'b', length: 2 };
Array.prototype.join.call(args, '-'); // "a-b"apply — when the arguments are already sitting in an array and I don't want to unpack them. Honestly, spread syntax has eaten most of this use case. Math.max.apply(null, nums) became Math.max(...nums) and nobody misses the old way. I mostly meet apply now when reading older code.
bind — when I'm handing a function to someone else and I won't be there when they call it. Event listeners, setTimeout, passing a method as a callback. Anything where the function leaves my hands.
setTimeout(user.greet.bind(user), 1000);That last one is the case that used to bite me constantly. The moment you write user.greet without the parentheses, the connection to user is gone. The function doesn't remember where it came from. bind is how you tie the rope back on before you throw it.
Where they quietly do nothing
This is the part I wish someone had told me on day one.
Arrow functions ignore all three. Completely. An arrow function takes this from wherever it was written, and no amount of call or bind will change it.
const f = () => console.log(this);
f.call({ x: 1 }); // still not { x: 1 }So if your fix is "just bind it" and nothing happens, check whether you're binding an arrow function. I lost a whole afternoon to this once.
bind only works once. A bound function is sealed. Bind it again, call it with something else, it won't budge.
const twice = f.bind({ x: 1 }).bind({ x: 2 });
twice(); // x is 1, the second bind is decorationnew beats bind. If you call a bound function with new, the bound this gets thrown away and a fresh object wins. Constructors don't negotiate.
Pre-filled arguments go in front, always. This one surprised me. Any extra arguments you pass to bind get glued to the front of the list forever. New arguments line up behind them, they never replace them.
function add(a, b) { return a + b; }
const addFive = add.bind(null, 5);
addFive(3); // 8
addFive(3, 9); // still 8, the 9 just falls off the endWhat I'd tell myself two years ago
this is decided at the moment you call a function, not where you wrote it. call, apply and bind are just the three ways of saying it out loud instead of hoping.
And if you find yourself binding everything defensively, that's usually a sign the code wants to be rearranged, not patched.
Working on something similar? Get in touch
Have questions about this article, architecture patterns, or looking for pair programming and engineering consulting? My inbox is always open.
Send me an emailRelated Articles
What Really Happens When You Call setTimeout()?
A Deep Dive from JavaScript to Hardware Every JavaScript developer has used setTimeout. It's one of the first async concepts we learn. But have you ever won...
Confusion will be gone about 'this'
Most confusion about `this` comes from one wrong assumption: that it's decided by where a function is **written**. It isn't. `this` is decided by how the function is **called**.
Understanding React’s Virtual DOM: The Secret Behind Lightning-Fast UIs
How React updates your apps in milliseconds, not seconds The Problem: Why DOM Updates Are Slow Imagine you’re building a social media feed. Every like, comment,...