call, apply & bind
Every JavaScript function inherits three methods from Function.prototype — call, apply, and bind — that let you control what this refers to when the function runs. They are the manual override for JavaScript’s normally implicit this binding, and they unlock two classic patterns: borrowing a method from one object to use on another, and pre-filling arguments to create specialized functions. Mastering them gives you precise control over execution context.
Why this matters
In a regular function, this is decided by how the function is called, not where it’s defined. When you pull a method off an object and call it standalone, the original this binding is lost. call, apply, and bind re-attach a specific object as this, so the function behaves as if it were invoked as a method of that object.
const user = {
name: "Ada",
greet() {
return `Hi, I'm ${this.name}`;
},
};
const greet = user.greet;
console.log(greet()); // this is undefined → error in strict mode
console.log(greet.call(user)); // correct binding
Output:
Hi, I'm Ada
call — pass arguments individually
call(thisArg, arg1, arg2, ...) invokes the function immediately, sets this to thisArg, and forwards the remaining arguments one by one. Use it when you already have your arguments as separate values.
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const person = { name: "Grace" };
console.log(introduce.call(person, "Hello", "!"));
Output:
Hello, I'm Grace!
apply — pass arguments as an array
apply(thisArg, argsArray) does the same thing as call, except the arguments are supplied as a single array (or array-like object). This is ideal when your arguments already live in an array.
function introduce(greeting, punctuation) {
return `${greeting}, I'm ${this.name}${punctuation}`;
}
const person = { name: "Linus" };
const args = ["Hey", "."];
console.log(introduce.apply(person, args));
Output:
Hey, I'm Linus.
A traditional use of apply was spreading an array into a variadic function, such as finding a maximum. Modern code usually prefers the spread operator, but the pattern is worth recognizing.
const numbers = [4, 9, 1, 7];
console.log(Math.max.apply(null, numbers)); // older style
console.log(Math.max(...numbers)); // modern equivalent
Output:
9
9
bind — create a permanently bound function
bind(thisArg, ...presetArgs) does not call the function. Instead it returns a new function with this permanently locked to thisArg. Any arguments you pass to bind are pre-filled, and you can supply more when the bound function is eventually called. The binding is fixed — calling call or apply on a bound function cannot change its this.
const counter = {
count: 0,
increment() {
this.count += 1;
return this.count;
},
};
const inc = counter.increment.bind(counter);
console.log(inc()); // works even when detached
console.log(inc());
Output:
1
2
This is why bind is the go-to fix for callbacks that lose their context, such as passing a method to setTimeout or an event listener.
Method borrowing
Sometimes one object has a useful method that another object lacks. Rather than copying the method, you can borrow it by setting this at call time. A classic example is using array methods on an array-like object such as arguments or a DOM NodeList.
function logArgs() {
// arguments is array-like, not a real array
const args = Array.prototype.slice.call(arguments);
return args.map((x) => x * 2);
}
console.log(logArgs(1, 2, 3));
Output:
[ 2, 4, 6 ]
Tip: In modern code, prefer
Array.from(arguments)or rest parameters (...args) overArray.prototype.slice.call. Method borrowing still matters when you genuinely have an object that isn’t a true array but exposeslengthand indices.
Partial application
Because bind pre-fills leading arguments, it’s a clean way to build specialized functions from general ones — a technique called partial application.
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(10));
console.log(triple(10));
Output:
20
30
Here null is passed as thisArg because multiply doesn’t use this; only the argument pre-filling matters.
Comparison
| Feature | call | apply | bind |
|---|---|---|---|
| Invokes immediately | Yes | Yes | No (returns a new function) |
| Arguments format | List: (thisArg, a, b) | Array: (thisArg, [a, b]) | List: (thisArg, a, b) (pre-filled) |
Sets this | Yes | Yes | Yes (permanently) |
| Supports partial application | No | No | Yes |
| Return value | Function’s result | Function’s result | A bound function |
Gotcha: When the target is an arrow function,
call,apply, andbindcannot change itsthis. Arrow functions capturethislexically at definition time, so thethisArgyou provide is silently ignored — only argument passing works.
Best Practices
- Reach for
callwhen arguments are already separate values, andapplywhen they’re already in an array. - Use
bindto lock context for callbacks (timers, event handlers) that would otherwise losethis. - Prefer the spread operator over
applyfor spreading arrays into variadic functions in modern code. - Pass
null(or any value) as the firstbind/callargument when the function ignoresthis, and focus on the arguments. - Avoid
Array.prototype.slice.call(arguments)in new code; use rest parameters orArray.frominstead. - Remember that arrow functions ignore an explicitly bound
this— don’t try to rebind them.