JavaScriptMedium

call() vs bind()

01

The Short Answer

Both call() and bind() are methods that allow you to manipulate the this context of a function. They enable you to control what the this keyword refers to inside the function being called.

The critical difference: call() invokes the function immediately, while bind() returns a new function with this permanently bound — you call it later.

02

How They Work

In the example below, we set up a person object with an introduce method that references this.name. The employee object has a different name but no method of its own. We'll use both call() and bind() to borrow person's method and run it with employee as the context.

basic-example.tstypescript
const person = {
  name: 'John',
  introduce: function (role: string, department: string) {
    return `Hello, I'm ${this.name}, ${role} in ${department}`;
  },
};

const employee = {
  name: 'Sarah',
};
03

The Difference Between call and bind

The key difference is timing. In the code below, call() executes the function right away and returns the result. bind() doesn't execute anything — it creates a brand new function with this permanently locked in, which you can store in a variable and call whenever you want. Notice how bind() can also pre-fill arguments at creation time.

call-vs-bind.tstypescript
// Using call() - executes immediately with arguments
person.introduce.call(employee, 'Manager', 'Sales');
// Returns: "Hello, I'm Sarah, Manager in Sales"

// Using bind() - creates a new function with bound context
const employeeIntroduce = person.introduce.bind(employee);
employeeIntroduce('Manager', 'Sales');
// Returns: "Hello, I'm Sarah, Manager in Sales"

// bind() can also pre-set arguments when creating the function
const salesIntroduce = person.introduce.bind(
  employee,
  'Manager',
  'Sales'
);
salesIntroduce(); // All arguments are pre-set
// Returns: "Hello, I'm Sarah, Manager in Sales"

In practical terms, use call() when you want to execute a function once with a different context, and use bind() when you want to create a reusable function with a specific context.

04

Partial Application with bind()

bind() can also pre-set arguments when creating the new function — a technique called partial application. In the code below, we create double and triple by binding the first argument of multiply to 2 and 3 respectively. Now each new function only needs one argument instead of two, making them more convenient to use repeatedly.

partial-application.tstypescript
function multiply(a: number, b: number): number {
  return a * b;
}

// Pre-set the first argument
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);

double(5);  // 10 (2 * 5)
double(12); // 24 (2 * 12)
triple(5);  // 15 (3 * 5)

// With call(), you'd have to pass both args every time
multiply.call(null, 2, 5);  // 10
multiply.call(null, 3, 5);  // 15
05

The Most Common Use Case: Event Handlers

The most common real-world use of bind() is preserving this when passing methods as callbacks. When you pass this.tick to setInterval, JavaScript detaches the method from its object — so this inside tick becomes undefined. Using bind(this) creates a new function where this is permanently locked to the Timer instance, fixing the problem.

event-handlers.tstypescript
class Timer {
  seconds = 0;

  start() {
    // Problem: this inside tick will be undefined
    setInterval(this.tick, 1000); // ❌ this.seconds is undefined

    // Solution: bind this permanently
    setInterval(this.tick.bind(this), 1000); // ✅ works
  }

  tick() {
    this.seconds++;
    console.log(this.seconds);
  }
}

// In React class components (before hooks):
class SearchBar extends React.Component {
  constructor(props) {
    super(props);
    // Without this line, this is undefined in handleChange
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.setState({ query: e.target.value });
  }
}
06

Important: bind() is Permanent

Once a function is bound with bind(), its this is locked permanently — you cannot override it. In the code below, we try to re-bind an already-bound function to a different object, but it has no effect. The first bind() always wins, which is important to know when debugging unexpected this behavior.

permanent-bind.tstypescript
const boundFn = person.introduce.bind(employee);

// Trying to re-bind to a different object — doesn't work
const anotherEmployee = { name: 'Mike' };
const reBound = boundFn.bind(anotherEmployee);
reBound('Dev', 'Engineering');
// Still returns: "Hello, I'm Sarah, Dev in Engineering"
// The original bind wins — this is permanently Sarah
07

Modern Alternative: Arrow Functions

Arrow functions capture this from their surrounding scope at the time they're defined (called lexical this). This means you don't need bind() at all — the arrow function automatically uses the correct this. In the Timer example below, the arrow function inside setInterval inherits this from the start() method, so it correctly references the Timer instance.

arrow-functions.tstypescript
class Timer {
  seconds = 0;

  start() {
    // Arrow function captures this from start()'s scope
    setInterval(() => {
      this.seconds++; // ✅ this is the Timer instance
      console.log(this.seconds);
    }, 1000);
  }
}

// In React — arrow functions as class properties
class SearchBar extends React.Component {
  // No bind needed — arrow function captures this automatically
  handleChange = (e) => {
    this.setState({ query: e.target.value });
  };
}
08

Why Interviewers Ask This

This question tests whether you understand this binding in JavaScript and can distinguish between immediate invocation and deferred execution. Interviewers want to see that you know when to use each: call() for one-off context switching, bind() for creating reusable bound functions. Mentioning the permanence of bind() and modern arrow function alternatives shows depth.

Quick Revision Cheat Sheet

call(): Executes immediately with a given this and arguments

bind(): Returns a new function with this permanently bound — call it later

Partial application: bind() can pre-set arguments: fn.bind(ctx, arg1)

Permanent: Once bound, this cannot be changed — not even by call() or another bind()

Modern alternative: Arrow functions capture this lexically — often replaces bind()