call, apply, bind

Difficulty: Intermediate

Question

What are call, apply, and bind in JavaScript? How do they differ and when would you use each?

Answer

call, apply, and bind are methods on Function.prototype that let you explicitly set 'this' for a function invocation.

call() invokes the function immediately with a given 'this' and arguments passed individually: fn.call(thisArg, arg1, arg2).

apply() is identical to call but takes arguments as an array: fn.apply(thisArg, [arg1, arg2]). A mnemonic: Apply takes an Array.

bind() does NOT invoke the function. Instead, it returns a new function with 'this' permanently bound. This is useful when you need to pass a function reference that should maintain a specific 'this' context, like event handlers or callbacks.

Common use cases include function borrowing (using a method from one object on another), partial application (pre-filling some arguments), and preserving context in callbacks.

Code examples

call vs apply vs bind

function introduce(greeting, punctuation) {
  return `${greeting}, I'm ${this.name}${punctuation}`;
}

const alice = { name: 'Alice' };
const bob = { name: 'Bob' };

// call - invoke with individual args
console.log(introduce.call(alice, 'Hello', '!'));

// apply - invoke with args array
console.log(introduce.apply(bob, ['Hi', '.']));

// bind - returns new function (does not invoke)
const aliceIntro = introduce.bind(alice, 'Hey');
console.log(aliceIntro('!!'));  // only need remaining args
console.log(aliceIntro('?'));   // reusable

call and apply invoke immediately. bind returns a new function. bind also supports partial application - pre-filling the first argument.

Function Borrowing

// Borrow Array methods for array-like objects
function getArgs() {
  // arguments is array-like but not an array
  const args = Array.prototype.slice.call(arguments);
  console.log(args);
  console.log(Array.isArray(args));
}
getArgs(1, 2, 3);

// Borrow methods between objects
const calculator = {
  total: 0,
  add(amount) {
    this.total += amount;
    return this;
  }
};

const myWallet = { total: 100 };

// Borrow add from calculator, use on myWallet
calculator.add.call(myWallet, 50);
console.log(myWallet.total); // 150

// Math.max with apply (before spread existed)
const nums = [5, 3, 8, 1, 9];
console.log(Math.max.apply(null, nums)); // 9
console.log(Math.max(...nums));          // 9 (modern)

Function borrowing lets you use methods from one object on another. Array.prototype.slice.call(arguments) was the classic way to convert array-like objects.

bind in Event Handlers and Classes

class Button {
  constructor(label) {
    this.label = label;
    this.clicks = 0;
    // Without bind, 'this' in handleClick would be the DOM element
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.clicks++;
    console.log(`${this.label} clicked ${this.clicks} time(s)`);
  }
}

const btn = new Button('Submit');
// Simulating event handler calls
btn.handleClick();
btn.handleClick();

// Partial application with bind
function multiply(a, b) {
  return a * b;
}
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
console.log(double(5));  // 10
console.log(triple(5));  // 15

bind in constructors ensures methods keep their 'this' when used as callbacks. Partial application with bind pre-fills arguments to create specialized functions.

Key points

Concepts covered

call, apply, bind, Function Borrowing, Partial Application, this