How call, apply, and bind work in JavaScript
Three methods on Function.prototype with the same goal - setting this. The differences are in (a) whether they invoke immediately, and (b) how you pass arguments. A quick rundown of which to use when.
call, apply, and bind are three methods on Function.prototype. All three exist for the same reason - to let you set the value of this in a function explicitly, regardless of how the function was called. This is the third of the four binding rules described in the post about this - the so-called explicit binding.
They differ in two things: whether they invoke the function immediately, and how you pass arguments.
| Method | Invokes | Args | Returns |
|---|---|---|---|
.call() | immediately | comma list | function’s return value |
.apply() | immediately | as an array | function’s return value |
.bind() | no - returns new fn | comma list | new fn with locked this |
A small demo:
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const adam = { name: "Adam" };
greet.call(adam, "Hi", "!"); // → "Hi, Adam!"
greet.apply(adam, ["Hi", "!"]); // → "Hi, Adam!"
const greetAdam = greet.bind(adam, "Hi");
greetAdam("!"); // → "Hi, Adam!"call and apply differ only in how arguments are passed. bind works differently: it doesn’t invoke the function, it returns a new one with this permanently bound. This is the so-called hard binding - once a function is bound, you can’t “unbind” it:
const bound = greet.bind(adam, "Hi", "!");
bound.call({ name: "Eve" }); // → "Hi, Adam!" (call ignored for this)#When to use which
In modern JS you see these methods rarely - newer syntax has replaced most of their classic use cases.
call- practically only when you need to invoke a function once with a specificthisand don’t want to create a new reference.apply- almost unused. It used to be handy for spreading an array into arguments (Math.max.apply(null, [1, 2, 3])), but since ES2015 spread does the job (Math.max(...[1, 2, 3])).bind- the most common of the three. You use it when you want to produce a version of a function with a permanentthisfor a later callback. In classes that react to DOM events, the alternative is arrow-as-property (and it’s more common in modern code).
In practice: if you’re writing in 2026 and not working on legacy code, you’ll see bind occasionally, call sometimes, and apply almost never.
#Historical curiosity
An old pattern worth understanding only when reading older code:
// Before, when arguments was the only option in a function with no rest params:
function legacy() {
const args = Array.prototype.slice.call(arguments);
// ...
}
// Today:
function modern(...args) {
// args is already a real array
}Method borrowing via call on a prototype barely makes sense anymore - [...arguments] or rest parameters do the job.