skip to content

In JavaScript, what is the difference between Function.prototype.call and Function.prototype.apply, and how does Function.prototype.bind differ from both?

level: juniorimportance: must knowfreq 80%

answer

  1. all three set this explicitly
  2. one of them does not invoke
  3. comma versus array
  4. returns a function, not a result

basics

~20 s

call and apply both invoke the function immediately with an explicit this; call takes the arguments individually, apply takes them as one array-like. bind invokes nothing — it returns a new function whose this is permanently fixed.

solid answer

~40 s

All three live on `Function.prototype` and all three let you choose the `this` value explicitly instead of relying on how the function is called. `call` and `apply` are identical except in how they take the arguments: `fn.call(thisArg, a, b)` spreads them out, `fn.apply(thisArg, [a, b])` takes a single array or array-like. Both invoke the function right there and return its result. `bind` is the odd one out — it does not invoke anything. `fn.bind(thisArg, a)` returns a brand-new function that, whenever it is later called, runs `fn` with `this` set to `thisArg` and with `a` already supplied as the first argument. A useful mnemonic is call = comma, apply = array, bind = build a function for later.

code

javascript · 12 lines
javascript
function greet(greeting, punctuation) {
  return greeting + ', ' + this.name + punctuation;
}

const user = { name: 'Ada' };

console.log(greet.call(user, 'Hi', '!'));     // 'Hi, Ada!'
console.log(greet.apply(user, ['Hi', '!'])); // 'Hi, Ada!'

const greetAda = greet.bind(user, 'Hi');
console.log(typeof greetAda);                 // 'function' - nothing ran yet
console.log(greetAda('!'));                   // 'Hi, Ada!'

go deeper

for a junior

Be able to state the signatures cold: call takes the receiver then comma-separated arguments, apply takes the receiver then one array, bind returns a new function instead of running anything.

for a middle

Explain what the returned bound function actually remembers, and how a strict-mode function treats a null or primitive thisArg differently from a sloppy-mode one.

for a senior

Show judgment about when explicit receiver control is the right tool at all, and flag the argument-count limit that makes apply or spread unsafe for very large arrays.

for a principal

Frame it as an API-design question: a library that accepts callbacks should decide deliberately whether it offers a thisArg parameter or expects callers to bind, and document that contract.

## The problem all three solve In JavaScript a function's `this` is not decided when the function is written — it is decided when the function is *called*. `obj.method()` gives `this === obj`, but pulling the same function out and calling it plainly gives something else entirely. `call`, `apply` and `bind` are the language's escape hatch: they let the *caller* state the receiver explicitly rather than letting the call syntax decide it. All three are methods on `Function.prototype`, so every function object inherits them. ## call and apply: same job, different argument shape ```js function greet(greeting, punctuation) { return greeting + ', ' + this.name + punctuation; } const user = { name: 'Ada' }; greet.call(user, 'Hi', '!'); // 'Hi, Ada!' — arguments listed out greet.apply(user, ['Hi', '!']); // 'Hi, Ada!' — arguments in one array ``` The first parameter of each is the `this` value; everything after it is the argument list. `call` takes them as ordinary comma-separated arguments; `apply` takes exactly one further value, an array or array-like object, and spreads it into the parameters. Semantically there is no other difference: both call the function synchronously and return whatever it returns. `apply` earns its keep when the arguments are already sitting in an array and you do not know how many there are: ```js const nums = [3, 1, 4, 1, 5]; Math.max.apply(null, nums); // 5 — Math.max takes separate arguments ``` Since ES2015 the spread syntax `Math.max(...nums)` does the same thing more readably, so new code rarely needs `apply` for this. It still appears when forwarding a whole argument list inside a generic wrapper. ## What happens to the thisArg The treatment of the first argument depends on whether the *called* function is strict: - In strict mode (which includes all module code and class bodies), the value is passed through untouched: `fn.call(null)` really does give `this === null`, and `fn.call(7)` gives the primitive number `7`. - In sloppy mode, `null` and `undefined` are replaced by `globalThis`, and primitives are boxed into wrapper objects, so `this` inside is a `Number` object rather than `7`. This is why `Math.max.apply(null, nums)` is harmless — `Math.max` ignores its receiver entirely — but passing `null` to a function that actually reads `this.something` will throw in strict mode. ## bind: build now, call later ```js const greetAda = greet.bind(user, 'Hi'); greetAda('!'); // 'Hi, Ada!' greetAda('?'); // 'Hi, Ada?' ``` `bind` returns a *new function object*. The original is untouched — `bind` never mutates anything. The returned function remembers two things: the `this` value you supplied, and any arguments you supplied after it, which are prepended to whatever arguments the caller passes later. Nothing runs until you invoke the result. That one difference — returns a function versus invokes immediately — is the whole reason all three exist. When you need to *hand a function to somebody else* (a callback, a stored handler, a value in a table) and still control its receiver, you need `bind`, because `call` and `apply` would have already run it. ## Choosing between them - You have the arguments written out and want to run it now → `call`. - You have the arguments in an array and want to run it now → `apply` (or spread with `call`). - You want a reusable function value with the receiver already decided → `bind`. A common mistake is `element.onclick = handler.call(obj)`, which assigns the *result* of calling `handler` immediately — usually `undefined` — rather than a function. `bind` is what that line wanted. ## Details worth knowing `call` and `apply` return the function's return value, so `Object.prototype.hasOwnProperty.call(obj, 'k')` is a safe way to invoke a method that a particular object might have shadowed or might not inherit at all. `apply` (and spread) build a real argument list, so passing a very large array can exceed the engine's argument limit and throw a `RangeError`; for hundreds of thousands of elements, loop or chunk instead.

  • Now that spread syntax exists, is there any reason left to use apply?
    Rarely for the classic `Math.max` case — `Math.max(...nums)` is clearer. It still shows up when forwarding an entire argument list inside a generic wrapper, where you want to pass along both a receiver and an already-collected array in one expression. Note that both spread and `apply` build a real argument list, so a very large array can blow the engine's argument limit and throw a `RangeError`.
  • What happens to a primitive or null passed as the thisArg?
    It depends on the called function's mode. Strict-mode functions — which includes everything in an ES module or a class body — receive the value exactly as given, so `this` can genuinely be `null` or the number `7`. Sloppy-mode functions substitute `globalThis` for `null`/`undefined` and box primitives into wrapper objects, so `this` becomes a `Number` object instead.

saying these in an interview costs you the question

  • Says bind calls the function immediately like call
  • Claims apply and call differ in what this they set
  • Thinks bind mutates the original function
  • Assigns handler.call(obj) where a function value is needed
  • Believes call is faster or apply is deprecated

context