Skip to content
JavaScript js functions 4 min read

call, apply & bind

Every JavaScript function inherits three methods from Function.prototypecall, 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) over Array.prototype.slice.call. Method borrowing still matters when you genuinely have an object that isn’t a true array but exposes length and 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

Featurecallapplybind
Invokes immediatelyYesYesNo (returns a new function)
Arguments formatList: (thisArg, a, b)Array: (thisArg, [a, b])List: (thisArg, a, b) (pre-filled)
Sets thisYesYesYes (permanently)
Supports partial applicationNoNoYes
Return valueFunction’s resultFunction’s resultA bound function

Gotcha: When the target is an arrow function, call, apply, and bind cannot change its this. Arrow functions capture this lexically at definition time, so the thisArg you provide is silently ignored — only argument passing works.

Best Practices

  • Reach for call when arguments are already separate values, and apply when they’re already in an array.
  • Use bind to lock context for callbacks (timers, event handlers) that would otherwise lose this.
  • Prefer the spread operator over apply for spreading arrays into variadic functions in modern code.
  • Pass null (or any value) as the first bind/call argument when the function ignores this, and focus on the arguments.
  • Avoid Array.prototype.slice.call(arguments) in new code; use rest parameters or Array.from instead.
  • Remember that arrow functions ignore an explicitly bound this — don’t try to rebind them.
Last updated June 1, 2026
Was this helpful?