Skip to content

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.

MethodInvokesArgsReturns
.call()immediatelycomma listfunction’s return value
.apply()immediatelyas an arrayfunction’s return value
.bind()no - returns new fncomma listnew 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 specific this and 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 permanent this for 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.

← Back to notes