Setting this explicitly
Every function has three built-in methods that let you pick this yourself.
| Method | Calls now | Arguments | Returns |
|---|---|---|---|
fn.call(thisValue, a, b) | yes | listed one by one | whatever fn returns |
fn.apply(thisValue, [a, b]) | yes | in an array | whatever fn returns |
fn.bind(thisValue, a) | no | optional presets | a new function |
The mnemonic for the middle one is that apply takes an array.
call and apply are for one-off calls, and bind is for handing a function to someone else to call later.
You need these the moment a function travels. Handing a method to setTimeout, to forEach, or to an event system is everyday code, and that hand-off is exactly where this gets lost.
bind is the standard repair, so this bug and its fix show up in nearly every interview loop.
One function, three receivers
The caller picks this on every line.
function introduce(greeting) { console.log(greeting + ", I am " + this.name); } const ada = { name: "Ada" }; const linus = { name: "Linus" }; introduce.call(ada, "Hello"); introduce.apply(linus, ["Hey"]); const introduceAda = introduce.bind(ada); introduceAda("Welcome");
Output
Hello, I am Ada Hey, I am Linus Welcome, I am Ada
introduce is not a method of anything. It is a standalone function, and neither object has a property pointing at it.
call and apply do the same work with different argument packaging, which the first two lines show directly.
bind is the odd one out, because it produces introduceAda without printing anything. The output appears only when that new function is called on the following line.
introduce itself is unchanged throughout. A bound function is a separate function object, and the original stays available for a different receiver.
Writing introduce("Hi") would be a plain call, so this is undefined in strict mode and this.name throws. These three methods exist precisely so a function like this can be used at all.
The classic bug: losing this in a callback
When you pass a method somewhere as a callback, like setTimeout(dog.speak, 100) or array.forEach(obj.method), the receiving code later calls it as a plain function.
That is rule 2 from lesson 2-1. The dot is long gone by the time the call happens, so this is lost.
The important detail is that the loss happens at the hand-off, not at the call. dog.speak evaluates to a bare function value the instant you write it, and the object is not carried along.
Two standard fixes cover almost every case.
dog.speak.bind(dog)locksthisbefore handing it over.- Wrapping it as
() => dog.speak()defers the lookup, so the arrow performs a real method call when invoked.
The second version stays live, which is occasionally what you want. It reads dog at call time, so reassigning dog changes what runs, while the bound version froze the receiver.
Unbound and bound, side by side
runLater calls whatever it is given as a plain function.
function runLater(callback) { callback(); } const dog = { name: "Rex", speak() { console.log(this.name + " says woof"); }, }; runLater(dog.speak); runLater(dog.speak.bind(dog));
Output
undefined says woof
Rex says woofThe unbound version loses this, and the bound one keeps it.
runLater is not doing anything unusual. Every callback-taking API in the language behaves this way, including forEach, setTimeout, and DOM event listeners.
Notice runLater cannot fix this from its side, which is why the responsibility sits with the caller. It has no way to know which object the function came from.
Some APIs do offer a receiver argument as a convenience. array.forEach(fn, thisArg) takes one, and bind is the general answer for the many that do not.
Binding twice does not rebind. fn.bind(a).bind(b) still uses a, because the second bind only wraps the already-locked function.
bind can also preset arguments
bind accepts arguments after the this value and locks those in too.
multiply.bind(null, 2) returns a function whose first parameter is permanently 2. The null just fills the this slot, since this function never uses this at all.
Making a specific function out of a general one this way is called partial application. The general function stays available, and you get a convenient specialized name alongside it.
Arguments passed to the bound function are appended after the preset ones, so the order is presets first, then whatever the call supplies.
apply has a classic use of its own. Math.max.apply(null, nums) hands an array to a function as separate arguments, and for years this was the standard way to take the max of an array.
Modern code writes Math.max(...nums) instead, using the spread syntax from unit 8. The apply version is worth recognizing anyway, because it fills older codebases and interview questions.
Presetting an argument and spreading an array
Two different jobs, neither involving a receiver.
function multiply(a, b) { return a * b; } const double = multiply.bind(null, 2); console.log(double(7)); console.log(double(50)); const nums = [3, 9, 2]; console.log(Math.max.apply(null, nums));
Output
14 100 9
double is multiply with its first argument frozen to 2, and apply feeds the whole array to Math.max as individual arguments.
The 7 in double(7) lands in b, because bound arguments fill the parameter list from the left.
Extra arguments are simply ignored here, so double(7, 99) still returns 14. multiply only declares two parameters, and both are already accounted for.
Math.max.apply(null, []) returns -Infinity, which is the correct identity for a maximum and still a surprise in a total. Guarding for an empty array is worth doing.
apply has a practical size limit, since arguments go on the call stack. Spreading an array of a million numbers can throw a range error, and a plain reduce loop is the safe alternative.
fn.bind(obj) returns a new function whose this is permanently obj, without calling anything yet.
That first word is the one people miss. bind never invokes the function, and forgetting the trailing call is a common bug that produces silence rather than an error.
call and apply are the ones that invoke immediately, and they return whatever the function returned.
The original fn is unchanged, so binding is not a mutation. You can bind the same function to five different objects and get five independent functions.
The lock is permanent, which matters for repeated binds. fn.bind(a).bind(b) keeps a, because the inner binding already fixed the receiver.
One practical consequence is worth remembering. Each bind creates a distinct function object, so el.removeEventListener("click", handler.bind(this)) never removes anything, and the bound reference has to be stored once and reused.
Fixing a lost receiver with bind
The notifier calls each handler as a plain function.
function notifyAll(handlers, message) { handlers.forEach((h) => h(message)); } const pager = { label: "pager", receive(message) { console.log(this.label + ": " + message); }, }; notifyAll([pager.receive.bind(pager)], "server down");
Output
pager: server down
notifyAll calls h(message), which is a plain call, so this is lost unless it is locked first.
Passing pager.receive on its own prints undefined: server down, and the single change is appending .bind(pager).
The arrow inside forEach does not help, because it inherits notifyAll's this rather than supplying one to h.
Wrapping instead of binding is equally valid here. Passing (m) => pager.receive(m) performs a real method call, and the trade is that the arrow has to forward the argument by hand.
Registries of callbacks are where this pattern earns its keep. Any array of handlers stored now and invoked later needs its receivers fixed at registration time, since the call site has no idea what they belong to.