Write a polyfill for Function.prototype.bind
`bind` returns a new function with `this` pre-set and optional args pre-applied. Polyfill: closure capturing thisArg + preset args, returning a function that calls the original via `apply`. Handle `new`: when called as constructor, ignore thisArg and use the freshly constructed `this`. Set `bound.prototype = Object.create(fn.prototype)` so instanceof checks work.
Cross-link to [[write-a-custom-implementation-of-functionprototypebind]] — same content, different slug.
Polyfill
if (!Function.prototype.myBind) {
Function.prototype.myBind = function (thisArg, ...preset) {
if (typeof this !== "function") throw new TypeError("not a function");
const fn = this;
function bound(...rest) {
const isNew = this instanceof bound;
return fn.apply(isNew ? this : thisArg, [...preset, ...rest]);
}
if (fn.prototype) bound.prototype = Object.create(fn.prototype);
return bound;
};
}Tests
function greet(g, name) { return `${g}, ${name} (this=${this.tag})`; }
greet.myBind({ tag: "A" }, "Hi")("Sam"); // "Hi, Sam (this=A)"
function Point(x, y) { this.x = x; this.y = y; }
const BoundPoint = Point.myBind(null, 10);
const p = new BoundPoint(20);
p.x; // 10
p.y; // 20
p instanceof Point; // trueKey spec behaviors covered
- Throws
TypeErrorif called on a non-function. new boundFn()ignores the boundthisArgand constructs the original.instance instanceof originalFnstill works.- Preset args + call-time args concatenate.
Not covered (skip unless asked)
bound.lengthshould beMath.max(0, fn.length - preset.length).bound.nameshould be"bound " + fn.name.- Spec says re-binding doesn't change the original
thisArg(our impl already preserves this via closure).
Why interviewers love this
It's a triple-tester: closures, this binding, prototype chain + new. Bonus signal if you handle the new case.
Interview framing
"Return a closure that calls fn.apply(thisArg, [...preset, ...rest]). The non-trivial part is the new case: when called as a constructor, the bound function should ignore thisArg and use the freshly created this. Detect with this instanceof bound and forward accordingly. Set bound.prototype = Object.create(fn.prototype) so instanceof checks still pass. Throw TypeError up front if called on a non-function. Spec niceties (bound.length, bound.name) usually skipped unless the interviewer probes."
Follow-up questions
- •What does new do on a bound function?
- •Why set bound.prototype?
- •How does our polyfill compare with native bind on length/name?
Common mistakes
- •Skipping the new case.
- •Not forwarding preset args.
- •Forgetting prototype setup.
Performance considerations
- •Closure allocation per bind. Cheap. Native is slightly faster due to engine intrinsics.
Edge cases
- •Non-function receiver (should throw).
- •Arrow functions ignore thisArg (lexical this).
- •Re-binding.
Real-world examples
- •Polyfill libraries (core-js), legacy IE support, React class binding patterns pre-arrow.