InterviewsVector
advancedVery common4 min read · Updated Jul 18, 2026

Implement Function.prototype.bind from Scratch

Implement Function.prototype.bind in JavaScript with partial application and correct new-operator handling — including the prototype link most polyfills get wrong.


The problem

Implement Function.prototype.myBind(thisArg, ...boundArgs) returning a new function that (1) calls the original with this = thisArg, (2) prepends boundArgs to call-time arguments, and (3) still works correctly with the new operator.

Requirements 1 and 2 are a closure exercise. Requirement 3 is the real question — it's where nearly every candidate (and a depressing number of blog polyfills) ships a bug that looks right and silently binds this to the wrong object.

Interviewers are evaluating: closures, this binding rules, prototype chains, and whether you actually test the edge case you claim to handle.

Where you see it in production

  • Callbacks that lose thissetTimeout(user.greet.bind(user), 1000); the bug class that made bind exist. (See the this keyword.)
  • Partial applicationconst log = console.log.bind(console, "[api]").
  • Pre-hooks-era Reactthis.handleClick = this.handleClick.bind(this) in every constructor.

Correct implementation

Function.prototype.myBind = function (thisArg, ...boundArgs) {
  if (typeof this !== "function") {
    throw new TypeError("myBind must be called on a function");
  }
  const targetFn = this;
 
  function boundFn(...callArgs) {
    // Was boundFn invoked with `new`? Then `this` is the freshly
    // constructed object and must NOT be replaced by thisArg.
    const calledWithNew = this instanceof boundFn;
    return targetFn.apply(
      calledWithNew ? this : thisArg,
      [...boundArgs, ...callArgs]
    );
  }
 
  // Link the prototype chain so `new boundFn()` instances are
  // instanceof targetFn — and so the check above can even work.
  if (targetFn.prototype) {
    boundFn.prototype = Object.create(targetFn.prototype);
  }
 
  return boundFn;
};

The bug almost everyone writes

The popular broken version checks against the wrong function:

// ❌ BROKEN — copied all over the internet
return function (...callArgs) {
  const calledWithNew = this instanceof targetFn; // wrong target!
  return targetFn.apply(calledWithNew ? this : thisArg, [...boundArgs, ...callArgs]);
};

Why it fails: new boundFn() creates an object whose prototype is boundFn.prototype — and if you never linked boundFn.prototype to targetFn.prototype, that object is not instanceof targetFn. The check is always false, so constructor calls silently bind to thisArg:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
 
const BoundPerson = Person.brokenBind(null, "Charlie");
const p = new BoundPerson(30);
p.name; // undefined ❌ — Person ran against globalThis, not p

With the correct implementation (prototype link + this instanceof boundFn):

const BoundPerson = Person.myBind(null, "Charlie");
const p = new BoundPerson(30);
p.name;               // "Charlie" ✅  boundArgs prepended
p.age;                // 30        ✅  call-time args appended
p instanceof Person;  // true      ✅  prototype chain intact

This pairing — the prototype link makes the instanceof check possible — is the insight the question exists to test. State it explicitly.

Dry run: the normal (non-new) path

function greet(greeting, punct) {
  return `${greeting}, I'm ${this.name}${punct}`;
}
const alice = { name: "Alice" };
 
const hi = greet.myBind(alice, "Hi");
// closure captures: targetFn = greet, thisArg = alice, boundArgs = ["Hi"]
 
hi("!");
// boundFn called normally → this is undefined/globalThis, not instanceof boundFn
// → targetFn.apply(alice, ["Hi", "!"]) → "Hi, I'm Alice!"

Edge cases interviewers probe

  • new after binding (the headline case above) — new wins over the bound this; bound arguments still apply.
  • Double bindingfn.bind(a).bind(b) keeps a. Your closure version gets this right for free: the inner bound function never lets b through. Worth saying aloud.
  • Arrow functions — binding an arrow function's this is a no-op (arrows have no own this), and new (arrow.bind()) throws because arrows aren't constructors.
  • boundFn identity — native bound functions have name: "bound greet" and a length of target.length - boundArgs.length (floored at 0). A polyfill can patch both with Object.defineProperty; knowing they exist matters more than coding them.
  • No prototype on the target — arrow functions and methods ({ m() {} }) have no .prototype; hence the if (targetFn.prototype) guard.
  • True spec behavior — a real bound function is an exotic object: it has no prototype property at all, and new walks through to the target's prototype at the engine level. Object.create is the closest userland approximation. Naming this gap honestly beats pretending the polyfill is exact.

Common mistakes

  • The this instanceof targetFn check without the prototype link (silently always-false).
  • Forgetting to link boundFn.prototype at all — instances lose the target's methods.
  • Assigning boundFn.prototype = targetFn.prototype directly — now mutating one mutates the other; Object.create adds the insulating layer.
  • Using an arrow function for boundFn — arrows can't be new-ed and have no this of their own, so the constructor path is unimplementable.
  • Prepending call args before bound args (order is [...boundArgs, ...callArgs]).

Follow-up questions

  • "Why this instanceof boundFn and not new.target?"new.target !== undefined is the modern, more precise check and works even when someone tampers with prototypes. Offer it as the upgrade.
  • "Implement bind using only call/apply?" — you just did; the interviewer may then chain into implementing apply itself.
  • "How is this different from partial application without this?" — see _.partial and currying.
  • "What does bind cost?" — one extra closure and one extra call frame per invocation; irrelevant almost always, measurable in hot paths (why React moved away from per-render binds).
  • "Why does fn.bind(a).bind(b) ignore b?" — the outer bound function's this is already fixed inside a closure the second bind can't reach.

  • The new Operator

    intermediate

    Implement myNew(Constructor, ...args): prototype linking, this binding, and the return-object override rule.

  • Same trick as call but with an arguments array — plus the subtle differences that interviewers probe.

  • Custom instanceof

    intermediate

    Walk the prototype chain by hand, then meet Symbol.hasInstance — and learn where instanceof lies to you.

  • Implement call from scratch: temporary method assignment and why Symbol keys avoid collisions.