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`.
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:
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:
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
.lengthequal tomax(0, fn.length - bound.length). - The returned function's
.nameshould 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 avoidsapply(since you can't use what you're polyfilling). Use thefn[symbolKey] = this; const res = thisArgsymbolKey; deletetrick. - apply polyfill: same trick, args as array.
- Why
Function.prototype.bindis 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
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).