MDN Function.prototype.bind bound function called as constructor

I know that when a function returned by Function.prototype.bind is called as a constructor, the pre-bound this is ignored. This is the behaviour specified in ECMA-262, and the behaviour that MDN polyfill implements. My question is: how that polyfill works in that case? I know that this code is responsoble for that:

fNOP = function () {}

and

return fToBind.apply(this instanceof fNOP && oThis
             ? this
             : oThis,

and

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

But why do they need to create this dummy function (fNOP), instantiate it and assign to the fBound's prototype and so on. Why can's they just write this:

fBound = function foo() {
      return fToBind.apply(this instanceof foo
             ? this
             : oThis,
             aArgs.concat(Array.prototype.slice.call(arguments)));
    };

?

Answers:

Answer

But why do they need to create this dummy function (fNOP), instantiate it and assign to the fBound's prototype and so on.

In case your bound function is called with new then this will be an instance of FNOP, this because the prototype of the returned function is an instance of FNOP.

this instanceof fNOP will be true if I invoke the returned value with new. But only because the returned value has an instance of FNOP as it's prototype.

If you would not create a dummy function and do:

    fBound = function () {
      return fToBind.apply(this instanceof fBound && oThis

Then how would you set fBound's prototype? You could try:

   fBound.prototype = new fToBind();

But the passed function may throw an exception when called without parameters, you could do:

   fBound.prototype = Object.create(fToBind.prototype);

But then you need the polyfil for Object.create as well and that polyfill basically does the same thing; create a dummy function, set it's prototype and create an instance of the dummy function to have an object with it's prototype set.

Answer

Polyfilling bind is hard, if not even impossible to do correclty. If you take a look at the spec, you notice that bound functions are Function objects

Clearly, that first quality is the most important, so what we do is to return a function that exhibits this behaviour. You already notice that not everything can be done properly, as .length of functions is non-writable, and prototype (implicitly created) is non-deleteable.

So how to implement [[Construct]]? We would need to determine whether the function was called with a new expression. This cannot be done reliably, as a new call could be faked with the help of .call()/.apply() and Object.create(). So what is usually done is testing Object.getPrototypeOf(this) === constructor.prototype, or more simple just this instanceof constructor. If required, we then would fake the [[Construct]] call of the to-be-bound function with the extended arguments.

So how to implement [[HasInstance]]? The only way to manipulate this is the value of the .prototype, which is used for the prototype chain lookups. To make fBound.[[HasInstance]] work the same way as fToBind.[[HasInstance]], we need to set fBound.prototype = fToBind.prototype.

However, if we do that, the is[[Construct]]ing check will fail us when the bound function is called on instances of the binded function. Hmm.

So, we will need to balance the trade-offs of the possible solutions. The MDN polyfill could be changed in the way you suggest, could be changed to pass Object.create(fToBind.prototype) instead of this, etc.

Property                      | current MDN |  your    | … with same
                              | polyfill    | solution | prototypes
------------------------------+-------------------------------------------
fBound(…) uses boundThis      | yes           yes        yes
                              | 
new fBound(…) ignores it      | yes           yes        yes
                              |
fBound.call(new fToBind)      | yes           yes        no
 uses boundThis               |
                              |
new fToBind instanceof fBound | no            no         yes
                              |
new fBound instanceof fBound  | yes           yes        yes
                              |
new fBound instanceof fToBind | yes           no         yes
                              |
Object.getPrototypeOf(new     | no            no         yes
 fBound)==fToBind.prototype   |

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.