Back to JavaScript
JavaScript
medium
mid

Can you explain how call, apply, and bind work in JavaScript?

`call(thisArg, ...args)` invokes immediately with a given `this`. `apply(thisArg, argsArray)` is the same but with args as an array. `bind(thisArg, ...partials)` returns a new function with `this` permanently set. The polyfill closes over `thisArg` + partials and uses `apply` (or `call`) internally — plus a `new.target` check so the bound function still works as a constructor.

5 min read·~15 min to think through

call, apply, and bind all set what this refers to inside a function. They differ in when and how.

invokes?args
fn.call(ctx, a, b)nowspread
fn.apply(ctx, [a, b])nowarray
fn.bind(ctx, a)returns a new fnpartial-applied

call and apply are interchangeable in practice — pick whichever matches how you have the args. bind is different: it produces a new function with this (and optionally leading args) locked in.

The polyfill.

js
Function.prototype.myBind = function (thisArg, ...partials) {
  const target = this;
  if (typeof target !== "function") {
    throw new TypeError("Bind must be called on a function");
  }

  function bound(...args) {
    // If called with `new`, ignore thisArg — `this` is the fresh instance.
    const isCtor = this instanceof bound;
    return target.apply(isCtor ? this : thisArg, [...partials, ...args]);
  }

  // Preserve prototype chain so `new boundFn()` still produces correct instances.
  if (target.prototype) {
    bound.prototype = Object.create(target.prototype);
  }
  return bound;
};

Why the new.target / instanceof check matters. The spec says: a bound function used as a constructor ignores the bound thisArg. Without that check, new BoundFn() would override the fresh instance with the bound context — instances would silently share state. Most candidate polyfills miss this.

Why preserve prototype. new boundFn() should produce something that's instanceof OriginalCtor. Linking bound.prototype to the target's prototype keeps the chain.

Partial application. bind takes args before the new function is called and prepends them on invocation:

js
const add = (a, b) => a + b;
const inc = add.bind(null, 1);
inc(5); // 6

This is partial application, not currying. Currying produces a chain of unary functions; bind fixes a known prefix and accepts the rest.

Common gotchas.

  • bind returns a new function each time — don't addEventListener(..., fn.bind(this)) then try to removeEventListener with a freshly bound version; the reference differs.
  • Arrow functions ignore bind for this — they capture lexically. bind can still set partial args, but thisArg is silently ignored.
  • call/apply with null or undefined in sloppy mode coerces this to the global object; in strict mode it stays null/undefined.

When to reach for which in real code. Almost never — arrow functions and lexical this have made manual bind mostly obsolete. The two remaining uses: (1) borrowing methods (Array.prototype.slice.call(arguments), though spread does this now) and (2) partial application without a helper library.

Follow-up questions

  • Why is the `new.target` check necessary in the bind polyfill?
  • Difference between partial application and currying.
  • How does `this` differ inside arrow functions vs regular functions?
  • Polyfill `call` and `apply` from scratch — what's the minimum needed?

Common mistakes

  • Forgetting the `new`-as-constructor case — bound function used with `new` should ignore `thisArg`.
  • Not preserving the prototype chain so `instanceof` breaks on bound constructors.
  • Using `bind` on arrow functions and expecting `this` to change.
  • Re-binding inside render and breaking referential equality for memoized children.

Performance considerations

  • Each `.bind()` allocates a new function object — avoid in hot paths and tight loops.
  • Arrow functions in class fields are typically faster than `bind` in constructor.

Edge cases

  • Calling `bind` on something that isn't a function — throw TypeError.
  • Binding a generator or async function — the polyfill should preserve that behavior (modern `Function.prototype.bind` does).
  • Binding twice — the second `bind`'s `thisArg` is silently ignored (the first one wins).

Real-world examples

  • Pre-class React class methods: `this.handleClick = this.handleClick.bind(this)`.
  • `Array.from(arrayLike)` replaces `Array.prototype.slice.call(arrayLike)` — both relied on `call` to borrow.