call, apply, and bind do, and how they differ;bind creates a permanently locked function, and where that solves real bugs;bind to build specialized functions;this rules from last time.Learn JS Series):Exercise 1 - method call versus detached call:
const dog = {
name: "Rex",
speak() { return `${this.name} says woof`; },
};
console.log(dog.speak()); // "Rex says woof" - rule 1, 'this' is dog
const detached = dog.speak;
// console.log(detached()); // ERROR (runtime): rule 2, 'this' is undefined
The insight: dog.speak() is a method call (rule 1), so this is the object before the dot; detached() is a plain call (rule 2) with this === undefined, and this.name then throws.
Exercise 2 - fixing this with call and bind:
function introduce() { return `I am ${this.name}`; }
const person = { name: "scipio" };
console.log(introduce.call(person)); // "I am scipio" - runs now
const bound = introduce.bind(person);
console.log(bound()); // "I am scipio" - locked function
The insight: call invokes immediately with a chosen this; bind returns a new function permanently tied to that this that you can store and call later.
Exercise 3 - regular callback versus arrow:
const box = {
size: 5,
reportLater() {
// setTimeout(function () { console.log(this.size); }, 10); // rule 2: undefined
setTimeout(() => { console.log(this.size); }, 10); // rule 5: keeps box
},
};
// box.reportLater(); // arrow prints 5; the regular function would fail
The insight: the regular callback is a plain call (rule 2, this undefined); the arrow uses the enclosing this (rule 5, the box), which is why it just works.
Right, with the five rules fresh, let's give rule 3 the full treatment it deserves. Last episode I called call, apply, and bind "a taste" and promised the real meal this time. Here it is. These three are the tools you reach for when you do NOT want to leave this to the mercy of the call site -- when you want to set it by hand, precisely, and know exactly what it will be.
Start with a fact that explains why these three even exist: in JavaScript, functions are objects (a Phase 3 detail we will unpack properly later, but take it on trust for now). Because a function is an object, it can carry methods of its own, and every function you ever write inherits three of them straight from Function.prototype: call, apply, and bind. You did not add them, they are just there, on every function, always.
All three answer the same question -- "what should this be when this function runs?" -- and they let YOU answer it instead of the call site. They differ along just two axes: whether they run the function right now or hand you a new function for later, and how they take the function's normal arguments. Get those two axes straight and there is genuinely nothing else to learn here. Let's take them one at a time.
call invokes the function immediately. Its first argument becomes this, and every argument after that is passed through to the function as its normal parameters, listed out one by one:
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const user = { name: "scipio" };
console.log(greet.call(user, "Hi", "!")); // "Hi, scipio!"
Read that call out loud as a sentence and it stops being mysterious: "call greet, set its this to user, and pass "Hi" and "!" as the ordinary arguments." That is the whole of call. It is the most direct way in the language to run a function with a this that you pick, right now, this instant.
One thing worth pinning down: the FIRST argument is special (it is the this value), and everything else shifts over to become the real parameters. So greet.call(user, "Hi", "!") maps user to this, "Hi" to greeting, and "!" to punctuation. Miscounting that offset is the single most common call mistake, so keep the mental picture that the first slot is "stolen" by this.
apply does exactly what call does, with one solitary difference: it takes the function's arguments as a single array instead of listed out individually. That is the only distinction between them. call spreads, apply packs:
console.log(greet.apply(user, ["Hey", "."])); // "Hey, scipio."
A memory aid that has stuck with me for years: apply takes an array, call takes a comma-separated list. Same first letter, same shape. Once that clicks you never mix them up again.
So when is packing the arguments into an array actually useful? When you already HAVE them in an array and do not want to unpack them by hand. The classic example is calling a function that expects separate numbers, on a list of numbers you already hold:
const nums = [5, 12, 8, 130, 44];
console.log(Math.max.apply(null, nums)); // 130 - array spread as separate args
console.log(Math.max(...nums)); // 130 - the modern spread equivalent
Math.max wants Math.max(5, 12, 8, ...), not an array, and before the spread operator existed, apply was THE way to feed it an array. Notice we passed null as the first argument, because Math.max does not use this at all, so any placeholder will do. In modern code the spread operator ... has made apply largely redundant (you can write greet.call(user, ...args) and get the same effect), but you will still meet apply constantly in existing code, so you must be able to read it fluently.
Here is where call and apply genuinely earn their keep, not as trivia but as a technique: method borrowing. If an object owns a useful method, you can run that method with a completely different object as its this, without any inheritance, without any shared prototype, without copying anything. You just point the method's this wherever you like:
const person = {
fullName() { return `${this.first} ${this.last}`; },
};
const other = { first: "scipio", last: "the great" };
console.log(person.fullName.call(other)); // "scipio the great"
other has no fullName method of its own. It does not inherit one. But we grabbed person's method and ran it with this aimed at other, and because fullName only ever touches this.first and this.last, it works perfectly. That is method borrowing in one line: a method is just a function that reads this, so give it whichever this you want.
This was historically vital for a very practical reason. Old-school JavaScript had "array-like" objects -- things with a length and numeric indices but WITHOUT the real array methods. The arguments object inside a function is the classic case. To use real array methods on them, you borrowed from Array.prototype:
function collectArgs() {
// borrow Array's slice to turn array-like 'arguments' into a real array
return Array.prototype.slice.call(arguments);
}
console.log(collectArgs(1, 2, 3)); // [1, 2, 3]
arguments is not a real array, so it has no .slice(). But slice only cares that its this has a length and indexed elements, so borrowing it via call works a treat. Modern code writes [...arguments] or a rest parameter (...args) in stead, but you will run into Array.prototype.slice.call(arguments) in older codebases forever, and now you know exactly what it is doing and why. Understanding this one idiom demystifies quit a lot of pre-2015 JavaScript.
Now for the different one. call and apply both run the function immediately. bind does NOT. Instead, bind returns a brand new function whose this is permanently locked to whatever you passed. It does not invoke anything -- it manufactures a new function for you to call whenever you please, and no matter how you later call it, its this cannot change:
const counter = {
count: 0,
increment() { this.count += 1; return this.count; },
};
const inc = counter.increment.bind(counter); // locked to counter forever
console.log(inc()); // 1 - works even though called as a plain function
console.log(inc()); // 2
That inc is a plain, bare function now -- there is no counter. in front of it -- and yet it still increments counter correctly, because the this is baked in. This is the definitive cure for the "lost this" bug we diagnosed last episode. Remember the trap: the moment you pass a method somewhere as a callback, it gets detached, rule 2 kicks in, and this becomes undefined. A bound method carries its this with it, so it survives the trip:
// safe to pass around: it will always have the right 'this'
setTimeout(counter.increment.bind(counter), 100); // increments correctly later
And the lock is genuinely permanent -- this is important, so let me show it. Once a function is bound, you cannot re-point its this even with call. The first bind wins, permanently:
function whoAmI() { return this.label; }
const a = { label: "A" };
const b = { label: "B" };
const boundToA = whoAmI.bind(a);
console.log(boundToA()); // "A"
console.log(boundToA.call(b)); // "A" - bind wins, call cannot override it
That permanence is exactly the property you want for event handlers and stored callbacks. A hugely common real-world pattern is to bind a method once in a constructor, so that every copy of that handler you hand out is already safe to detach:
class Button {
constructor(label) {
this.label = label;
this.handleClick = this.handleClick.bind(this); // lock 'this' once, up front
}
handleClick() {
return `clicked ${this.label}`;
}
}
const save = new Button("Save");
const handler = save.handleClick; // detached, but already bound in the constructor
console.log(handler()); // "clicked Save" - survives detachment
Before arrow functions existed, bind was THE canonical way to keep this correct in callbacks, and you will see that constructor-binding pattern in mountains of older class-based code (React components from the mid-2010s are packed with it). Arrows now handle many of those cases more cleanly, but bind is still the right tool when you need a reusable, detachable function that is permanently welded to a specific object.
bind has a bonus feature that is easy to miss and genuinely useful. Besides fixing this, any EXTRA arguments you pass to bind get pre-filled into the returned function, locked in ahead of time. This lets you manufacture a specialized function with some of its arguments already baked in:
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2); // 'this' unused, so null; a is fixed to 2
console.log(double(5)); // 10 - calls multiply(2, 5)
console.log(double(10)); // 20
const triple = multiply.bind(null, 3);
console.log(triple(5)); // 15
We passed null for this (because multiply never touches this, so it does not matter), and pre-filled a as 2. The result is a double function that only needs b. We just built a new, more specific function out of a general one by freezing one of its arguments. That technique has a name -- partial application -- and it previews the currying ideas coming a few episodes down the line. bind hands you a taste of it for free, no library required.
You can pre-fill more than one argument, too. Fix both and you have effectively created a constant-producing function:
function label(prefix, level, msg) {
return `${prefix}[${level}] ${msg}`;
}
const warn = label.bind(null, "APP", "WARN"); // pre-fill prefix and level
console.log(warn("disk almost full")); // "APP[WARN] disk almost full"
console.log(warn("cache miss")); // "APP[WARN] cache miss"
The arguments fill left to right, so bind's pre-filled values always take the FIRST parameter slots, and whatever you pass at call time fills the rest. That ordering (fixed arguments first, variable arguments last) is worth remembering, because it is exactly why library functions meant for partial application put their "configuration" parameters first and their "data" parameter last.
Since arrow functions lock this lexically (rule 5 from last episode), they quietly replace bind in a whole category of situations. If you are writing a callback INSIDE a method and all you want is the surrounding this, an arrow is cleaner than binding anything:
const service = {
name: "api",
logLater() {
// no bind needed - the arrow keeps logLater's 'this'
setTimeout(() => console.log(`${this.name} ready`), 10);
},
};
// service.logLater(); // "api ready"
So when do you still reach for call, apply, or bind? Three cases, and they are worth memorizing as a checklist. First, when you need to run a function with a specific object's this right now -- that is method borrowing, and only call/apply do it. Second, when you need a REUSABLE, DETACHABLE function permanently locked to an object -- event handlers, callbacks you store for later, anything you hand off that must not lose its this -- that is bind. Third, when you want partial application -- again bind. Arrows cover exactly one thing: "keep whatever this I already have here." These three cover the other thing: "set this to something specific." They are not competitors, they solve different problems.
A good few of you came to this series from the Learn Python Series, with some Rust and Go folks mixed in, so a look sideways helps put JavaScript's design in perspective. The short version: other languages tend to give you partial application and stable receivers through closures and bound methods, without needing a special bind.
Python has a bound method for free -- pulling a method off an instance keeps the instance attached, so the JavaScript detachment bug simply does not exist. And for partial application it ships functools.partial, which is bind's argument-fixing feature as a standalone tool:
from functools import partial
def multiply(a, b):
return a * b
double = partial(multiply, 2) # pre-fill the first argument, like bind
print(double(5)) # 10
print(double(10)) # 20
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
c = Counter()
inc = c.increment # a bound method - remembers c, no bind() needed
print(inc()) # 1
Notice Python splits the two jobs bind does into two mechanisms: the bound method handles self, and partial handles pre-filled arguments. JavaScript's bind bundles both into one call.
Rust does not have this at all in the wandering JavaScript sense -- the receiver is an explicit self parameter checked by the compiler. For the "specialize a function by fixing an argument" job, you reach for a closure, which captures the fixed value:
fn multiply(a: i32, b: i32) -> i32 {
a * b
}
fn main() {
let double = |b| multiply(2, b); // a closure captures the fixed argument
println!("{}", double(5)); // 10
println!("{}", double(10)); // 20
}
The closure |b| multiply(2, b) IS partial application -- it is just spelled with Rust's closure syntax rather than a bind method. Same idea, different clothes.
Go, minimalist as ever, gives you method values (a method pulled off a value keeps its receiver) and plain closures for fixing arguments:
package main
import "fmt"
func multiply(a, b int) int {
return a * b
}
func main() {
double := func(b int) int { return multiply(2, b) } // closure fixes a = 2
fmt.Println(double(5)) // 10
fmt.Println(double(10)) // 20
}
Three languages, one recurring pattern: partial application is really just a closure that remembers a fixed value, and a stable receiver is really just a method that stays attached to its object. JavaScript's call/apply/bind are the explicit, verbose versions of ideas that other languages fold into closures and bound methods. Having said that, once you see bind as "closure that also fixes this", the whole family clicks into place and stops feeling like special magic. ;-)
Three exercises, increasing in difficulty. Type them out and predict the output before you run them -- the gap between your guess and the actual result is where the real learning happens. Full solutions open the next episode.
sayCity() that returns this.city, and two objects { city: "amsterdam" } and { city: "berlin" }. Use call to run sayCity with each. Then rewrite one of the calls using apply and note the only difference between the two.multiply(a, b, c) that returns a * b * c, and use bind to create a times12 function that fixes a and b to 3 and 4, so times12(2) returns 24. Explain in one line what you passed as bind's FIRST argument and why.const logger = { prefix: "[LOG]", write(msg) { return this.prefix + " " + msg; } }, create a detachable write function bound to logger and pass it into a setTimeout. Show it still uses the right prefix, and explain why an unbound version would fail using rule 2.call, apply, and bind from Function.prototype; all three let YOU decide what this will be, in stead of the call site.call runs the function now with a chosen this and comma-separated arguments; apply is identical but takes the arguments as an array (apply = array).call/apply to run one object's method with another object as this -- no inheritance needed, because a method is just a function that reads this.bind returns a NEW function permanently locked to a this (and the lock cannot be overridden, even by call), which is the definitive fix for the "lost this" bug when passing methods as callbacks.bind are pre-filled left to right (partial application), letting you build specialized functions like double = multiply.bind(null, 2).bind when you just want the surrounding this; use call/apply/bind when you must set a SPECIFIC this or partially apply arguments.Next episode we change gears completely and look at recursion: functions that call themselves, the base case that stops them from running forever, how the call stack actually grows with each call, and the dreaded stack overflow that happens when it grows too far.