6Implement Function.prototype.bind(), including partial arguments
Problem
Implement Function.prototype.myBind(thisArg, ...boundArgs) without using the built-in bind. It returns a new function that, when called, runs the original with this set to thisArg and with boundArgs placed before any arguments passed later.
function volume(l, w, h) {
return l * w * h * this.scale;
}
const scaled = volume.myBind({ scale: 2 }, 3); // l is fixed to 3
scaled(4, 5); // 3 * 4 * 5 * 2 = 120Requirements
- Support partial application: arguments given to
myBindcome first, call-time arguments after them. - The bound
thiscannot be changed later, for example by calling the bound function as a method of another object. - When the bound function is used with
new, ignore the boundthisand construct a normal instance of the original function. - Throw a
TypeErrorwhenmyBindis called on something that is not a function.
Try it yourself
Write your solution, run it, then submit to check it against the tests.
Press Run or ⌘ Enter to execute your code.
Video walkthroughs
Stuck? Watch someone solve it, then try again on your own.
Step by step
Build the solution one idea at a time.
- 1
Capture the original function
Inside
myBind,thisis the function being bound. Save it astarget, becausethisinside the returned function will be something else. - 2
Return a closure
The returned function closes over
target,thisArgandboundArgs. When it is called, it combinesboundArgswith the new arguments. - 3
Call the target with the fixed this
Use
target.apply(thisArg, [...boundArgs, ...args]). BecausethisArgcomes from the closure, whateverthisthe bound function was called with is ignored. - 4
Handle new
new.targetis set only when a function is called withnew. In that case, construct the target instead:Reflect.construct(target, args, new.target)creates a proper instance and runs the constructor, ignoring the boundthis. - 5
Keep instanceof working
Set
bound.prototypeto an object that inherits fromtarget.prototype, so instances created through the bound function are alsoinstanceof target.
Approach
bind is a closure factory: remember the function, the this value and the first arguments, and return a function that merges arguments and calls the original. The only special case is new, where the bound this must be ignored.
Solution
Function.prototype.myBind = function (thisArg, ...boundArgs) {
if (typeof this !== "function") {
throw new TypeError("Bind must be called on a function");
}
const target = this;
function bound(...args) {
const allArgs = [...boundArgs, ...args];
if (new.target) {
// Called with new: build a real instance and ignore the bound this.
return Reflect.construct(target, allArgs, new.target);
}
return target.apply(thisArg, allArgs);
}
if (target.prototype) {
bound.prototype = Object.create(target.prototype);
}
return bound;
};Complexity
Time: O(b + a) per call to copy the bound and new arguments. Space: O(b) for the stored arguments, kept alive by the closure.
Edge cases
- Bound arguments always come first:
fn.myBind(null, 1)(2, 3)callsfn(1, 2, 3). - Calling the bound function as
obj.method()does not changethis. - Binding a bound function again cannot change
this; only the first bind wins, but extra arguments still stack up. - With
new, the boundthisis ignored and the instance isinstanceofthe original constructor. - Arrow functions have no own
this, so binding them only fixes arguments.
Follow-up questions
Answer each one out loud first, then reveal the model answer.