FrontendPro

6Implement Function.prototype.bind(), including partial arguments

easyAbout 15 minutesCodingFreethisclosuresfunctionspolyfills

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 = 120

Requirements

  • Support partial application: arguments given to myBind come first, call-time arguments after them.
  • The bound this cannot 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 bound this and construct a normal instance of the original function.
  • Throw a TypeError when myBind is called on something that is not a function.

Try it yourself

Write your solution, run it, then submit to check it against the tests.

solution.jsJavaScript

Press Run or ⌘ Enter to execute your code.

Video walkthroughs

Stuck? Watch someone solve it, then try again on your own.

call, apply and bind in JavaScript | Polyfill for bind method | JavaScript Interview QuestionsAkash Ingole on YouTubeOpen on YouTube
Writing polyfills for call(), apply() and bind() methods in JavaScript | JS Interview QuestionAnkit Saxena on YouTubeOpen on YouTube

Step by step

Build the solution one idea at a time.

  1. 1

    Capture the original function

    Inside myBind, this is the function being bound. Save it as target, because this inside the returned function will be something else.

  2. 2

    Return a closure

    The returned function closes over target, thisArg and boundArgs. When it is called, it combines boundArgs with the new arguments.

  3. 3

    Call the target with the fixed this

    Use target.apply(thisArg, [...boundArgs, ...args]). Because thisArg comes from the closure, whatever this the bound function was called with is ignored.

  4. 4

    Handle new

    new.target is set only when a function is called with new. In that case, construct the target instead: Reflect.construct(target, args, new.target) creates a proper instance and runs the constructor, ignoring the bound this.

  5. 5

    Keep instanceof working

    Set bound.prototype to an object that inherits from target.prototype, so instances created through the bound function are also instanceof 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) calls fn(1, 2, 3).
  • Calling the bound function as obj.method() does not change this.
  • Binding a bound function again cannot change this; only the first bind wins, but extra arguments still stack up.
  • With new, the bound this is ignored and the instance is instanceof the 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.