Implement Function.prototype.bind from scratch
The Short Answer
Function.prototype.bind returns a brand-new function that, when called, runs the original function with this permanently locked to the value you passed. It also lets you pre-fill leading arguments (partial application). Crucially, bind does not call the function — it just hands you back a bound copy you can invoke later.
Writing your own bind is a favourite interview task because it forces you to combine three concepts at once: how this is resolved, how apply forwards arguments, and how closures capture state. The snippet below shows the native behaviour we are trying to reproduce, so keep it as the reference for what our polyfill must match.
const user = { name: "Ada" };
function greet(greeting: string, punctuation: string) {
return `${greeting}, ${this.name}${punctuation}`;
}
const greetAda = greet.bind(user, "Hello");
console.log(greetAda("!")); // "Hello, Ada!" — this.name is locked to "Ada"
What bind Must Do
Before writing code, it helps to spell out the contract our polyfill is bound by. Each of these requirements maps directly to a line in the implementation, so read them as a checklist we will tick off.
Requirements
- Return a new function instead of calling the original immediately
- Lock `this` inside the original to the context passed to bind
- Remember any leading arguments and prepend them on every later call
- Append the arguments passed at call time after the bound ones
- Forward the return value of the original function back to the caller
A First Implementation
The core idea is a closure. We capture the original function (this inside myBind) plus the bound arguments, then return a new function that merges the bound arguments with the call-time arguments and invokes the original via apply. Notice how apply is what lets us both set the context and spread an array of arguments in one step.
Function.prototype.myBind = function (context, ...boundArgs) {
const originalFn = this; // the function bind was called on
return function (...callArgs) {
// merge pre-filled args with the ones passed at call time
return originalFn.apply(context, [...boundArgs, ...callArgs]);
};
};
// usage
const greetAda = greet.myBind(user, "Hello");
console.log(greetAda("!")); // "Hello, Ada!"
Why apply and not call
We use apply because the merged arguments live in an array. apply accepts an array directly, whereas call would force us to spread it ourselves. Either works — apply is just the cleaner fit here.
Handling the new Keyword
There is a subtle edge case strong candidates mention: a bound function can still be used as a constructor with new. When that happens, the spec says the bound this should be ignored in favour of the freshly created instance. The version below detects construction and rebinds this correctly, which is what separates a textbook answer from a complete one.
Function.prototype.myBind = function (context, ...boundArgs) {
const originalFn = this;
function boundFn(...callArgs) {
// when called with `new`, `this` is the new instance, not `context`
const isNewCall = this instanceof boundFn;
return originalFn.apply(
isNewCall ? this : context,
[...boundArgs, ...callArgs]
);
}
// keep the prototype chain so instanceof still works
if (originalFn.prototype) {
boundFn.prototype = Object.create(originalFn.prototype);
}
return boundFn;
};
The key line is this instanceof boundFn. When the bound function is invoked with new, JavaScript sets this to the new object whose prototype links back to boundFn, so the check is true and we ignore the original context. For normal calls it is false and the bound context wins.
Common Mistakes
Calling the function instead of returning one
Returning `originalFn.apply(context, args)` directly makes your bind behave like call — it runs immediately and returns a value, not a reusable function.
✅Return a new wrapper function and only invoke the original inside that wrapper.
Dropping the pre-bound arguments
Forgetting to merge `boundArgs` with `callArgs` means partial application silently breaks — the leading arguments you pre-filled never reach the function.
✅Always spread both arrays together: `[...boundArgs, ...callArgs]`.
Why Interviewers Ask This
Implementing bind is a compact way to prove you truly understand this, closures, and argument forwarding rather than just memorising syntax. Interviewers also watch for whether you remember the partial-application and new edge cases — mentioning them, even briefly, signals depth beyond the happy path.
Quick Revision Cheat Sheet
Returns: A new function — does not invoke the original immediately
this: Locked to the context passed to bind (unless called with new)
Partial application: Leading args are remembered and prepended on every call
Core mechanism: Closure captures the original fn + bound args, then uses apply
new edge case: Use `this instanceof boundFn` to ignore the bound context