Back to JavaScript
JavaScript
medium
very high
mid

How would you implement a polyfill for Function.prototype.bind?

Return a new function that calls the original with a fixed `this` and partially applied args. Handle the `new`-as-constructor case so the bound `this` is ignored when called with `new`.

6 min read·~20 min to think through

bind returns a new function with this permanently set and (optionally) leading arguments pre-supplied. The naive answer (fn.apply(thisArg, args)) handles 80% — the remaining 20% (constructor calls, prototype chain, length/name) is what interviewers grade on.

Naive (and mostly correct) version:

ts
Function.prototype.myBind = function (thisArg: any, ...bound: any[]) {
  const fn = this; // the function being bound
  return function (...called: any[]) {
    return fn.apply(thisArg, [...bound, ...called]);
  };
};

That's enough for everyday use. Interviewers then ask: "What if someone does new boundFn()?"

The constructor case. The spec says: when a bound function is called with new, the bound this is ignored, and new fn(...) runs against the original. The trick to detect this in user-land: new.target is non-undefined inside a function called with new, OR this instanceof returnedFn is true.

Production-grade polyfill:

ts
Function.prototype.myBind = function (thisArg: any, ...bound: any[]) {
  if (typeof this !== "function") throw new TypeError("Bind must be called on a function");
  const fn = this;
  function Bound(this: any, ...called: any[]) {
    // 'new Bound()' → new.target is Bound; route the construction to fn
    const isCtor = new.target !== undefined;
    return fn.apply(isCtor ? this : thisArg, [...bound, ...called]);
  }
  // Preserve prototype so 'new Bound()' instances pass instanceof Original
  if (fn.prototype) {
    Bound.prototype = Object.create(fn.prototype);
  }
  return Bound as any;
};

Why Object.create(fn.prototype)? When new Bound() runs, the new object's [[Prototype]] should be fn.prototype so instance instanceof Fn is true. Setting Bound.prototype to Object.create(fn.prototype) makes the prototype chain go through.

Spec niceties (rarely required to demo, but worth mentioning):

  • The returned function should have .length equal to max(0, fn.length - bound.length).
  • The returned function's .name should be "bound " + fn.name.
  • Both are read-only on real bound functions; in user-land you'd use Object.defineProperty.

Interview follow-ups.

  • call polyfill: fn.myCall = function(thisArg, ...args) { return this.apply(thisArg, args); } — but the real polyfill avoids apply (since you can't use what you're polyfilling). Use the fn[symbolKey] = this; const res = thisArgsymbolKey; delete trick.
  • apply polyfill: same trick, args as array.
  • Why Function.prototype.bind is slow in tight loops: every call creates a new function; for hot paths, store the bound version once.

When to use bind in modern code. Mostly historical — arrow functions and useCallback cover most cases. Still useful for: event listeners that need a stable reference, partial application without a separate library, and React class components (legacy).

Code

ts
Function.prototype.myCall = function (thisArg: any, ...args: any[]) {
  if (typeof this !== "function") throw new TypeError();
  thisArg = thisArg ?? globalThis;
  const key = Symbol("fn");
  (thisArg as any)[key] = this;
  const result = (thisArg as any)[key](...args);
  delete (thisArg as any)[key];
  return result;
};
call() polyfill — without using apply

Follow-up questions

  • How does your bind handle being called with new?
  • Implement call without using apply (and vice versa).
  • What does Function.prototype.bind set on .name and .length?
  • Why is the bound function's prototype derived from the original?

Common mistakes

  • Using arrow function for the wrapper — `this` from the call site is lost; new.target check fails.
  • Forgetting partial application (the bound args before the call args).
  • Not handling the `new` case — bound functions used as constructors silently misbehave.
  • Mutating fn.prototype directly instead of Object.create(fn.prototype).

Performance considerations

  • bind allocates a closure on every call — hot loops should bind once and reuse.
  • Native bind is faster than user-land polyfills; prefer it when available.

Edge cases

  • Binding null/undefined as thisArg in non-strict mode → globalThis. In strict mode → undefined is preserved.
  • Calling with new on a bound arrow function throws (arrows have no [[Construct]]).
  • bind on a function with no .prototype (arrow) → cannot construct.

Real-world examples

  • Legacy React class components binding handlers in the constructor: this.handle = this.handle.bind(this).

Senior engineer discussion

Senior signal: handling new.target, prototype chain preservation, and articulating the spec details (length/name) even if you don't implement them.

Related questions