HuntExams Academy logo
JavaScriptIntermediate#functions#this

Difference between call, apply and bind?

Answer

call and apply invoke the function immediately with an explicit this — call takes arguments individually, apply takes them as an array. bind returns a new function with this (and optional arguments) permanently fixed.

Example

function greet(a, b){ return `${this.name}${a}${b}`; }
greet.call({name:'X'}, '!', '?');
greet.apply({name:'X'}, ['!', '?']);
const g = greet.bind({name:'X'});
g('!', '?');

Detailed explanation

JavaScript’s call, apply, and bind are methods inherited from Function.prototype that let you control how a function receives its this value. For ordinary functions, this usually depends on the invocation rather than where the function was defined. These methods matter when borrowing methods, invoking functions with dynamically assembled arguments, or passing object methods as callbacks. The central distinction is timing: call and apply execute the target function immediately, while bind creates a new function for later execution.

call accepts a thisArg followed by individual arguments: greet.call({ name: 'X' }, '!', '?'). apply accepts a thisArg followed by an array or array-like object containing arguments: greet.apply({ name: 'X' }, ['!', '?']). With the given greet function, both return 'X!?'. They return whatever the target function returns and propagate any exception it throws. apply does not accept every iterable directly; for example, a Set should first be converted to an array. Modern spread syntax often replaces apply for argument expansion, but greet(...args) does not explicitly supply a receiver; greet.call(receiver, ...args) does.

bind stores the target function, supplied receiver, and any leading arguments in a bound function. For example, const g = greet.bind({ name: 'X' }, '!') creates a function where g('?') returns 'X!?'. Later arguments are appended after the bound arguments, making bind useful for partial application. Calling g.call(otherObject, '?') cannot replace its bound receiver. The receiver is stored by reference, not copied, so changing its name property affects subsequent calls. Binding does not execute greet or modify the original function.

Interviewers also expect the important exceptions. Arrow functions capture this lexically, so call, apply, and bind cannot replace their this, although arguments still work. For strict-mode ordinary functions, the supplied thisArg is preserved exactly; non-strict functions substitute the global object for null or undefined and box primitive receivers. Finally, when a bound function is used with new, if its target is constructible, the bound receiver is ignored and a fresh instance becomes this. Bound leading arguments still apply.

Key points to remember

  • call invokes immediately with individual arguments: fn.call(receiver, a, b).
  • apply invokes immediately with an array or array-like argument list: fn.apply(receiver, args).
  • bind returns a new function and can fix both its receiver and leading arguments.
  • call and apply return the target’s result; bind returns a function, not that result.
  • Arrow functions ignore receiver overrides; constructor invocation ignores a bound receiver.

Common mistakes candidates make

  • Passing an argument array to call and expecting it to be automatically unpacked.
  • Using bind when immediate execution is required, without invoking the returned function.
  • Claiming bind freezes the receiver object or can override an arrow function’s lexical this.

Likely follow-up questions

Why is bind useful when passing an object method as a callback?

Passing obj.method alone separates the function from its receiver, so the callback’s caller determines this. Passing obj.method.bind(obj) preserves obj as the receiver for ordinary calls.

Can you change a bound function’s this by binding it again?

No, rebinding cannot replace the receiver already stored by the original bound function. It can add more bound arguments, which follow the arguments bound earlier.

How does apply differ from spread syntax?

apply takes an array-like argument list and explicitly supplies this, while spread expands an iterable into arguments. Use fn.call(receiver, ...args) when you need both iterable expansion and an explicit receiver.

Interview tip: Lead with invocation timing and argument format, demonstrate all three using greet, then mention arrow functions and the new exception.

Related Interview Questions

1
JavaScriptIntermediate#functions#es6

Difference between arrow functions and regular functions?

Open
2
JavaScriptIntermediate#prototypes#oop

What is prototypal inheritance?

Open
3
JavaScriptIntermediate#oop#es6

What is the difference between an ES6 class and a constructor function?

Open