Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A problem with Function.prototype.bind polyfill

I met an issue with the common polyfill for Function.prototype.bind, found on the MDN, and I do not know where to fix it.

Here is the polyfill:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

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

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

    return fBound;
  };
}

Context

I was trying to unit test some React components usign a Mocha / PhantomJS wrapper for Gulp. This gulp task uses PhantomJS v1.9, that does not have Function.prototype.bind support (source). And Function.prototype.bind is used some times in React source code, so I used the polyfill.

The issue

When I ran my script, I received the following error:

TypeError: instanceof called on an object with an invalid prototype property.

After some investigations, I found that there is a specific call of bind from React that made the polyfill crash (link):

var HTMLDOMPropertyConfig = {
  isCustomAttribute: RegExp.prototype.test.bind(
    /^(data|aria)-[a-z_][a-z\d_.\-]*$/
  )
  // [...]
};

RegExp.prototype.test is a native function and has no prototype, so the line fNOP.prototype = this.prototype; was setting fNOP's prototype to undefined, which made the test this instanceof fNOP throw the error: the right operand of instanceof must have an object prototype.

After having discussed with @rauschma on Twitter, he told me that this while fNOP's related code is certainly here to deal with using a bound function as a constructor, which makes sense.

The solution

I think that just testing that the function to bind has actually a prototype before setting it as fNOP.prototype would solve the issue. So the fixed version would be:

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== 'function') {
      // closest thing possible to the ECMAScript 5
      // internal IsCallable function
      throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
    }

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

    // test this.prototype in case of native functions binding:
    if (this.prototype)
      fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();

    return fBound;
  };
}

My problem is that:

  1. I do not know who is the original author
  2. I do not know if there is an existing tests suit for polyfills
  3. I do not know where to submit that fix if is the good way to solve my problem

A quick Github search shows that this polyfill is used in many places, and if it fails with binding native functions, it looks a bit dangerous to let that out.

@DavidBruant

This comment has been minimized.

Copy link

@DavidBruant DavidBruant commented Dec 12, 2014

I do not know who is the original author

Possibly John-David Dalton?

I do not know if there is an existing tests suit for polyfills

Standards test suites ;-)
https://github.com/tc39/test262/
https://github.com/tc39/test262/tree/master/test/built-ins/Function/prototype/bind
(may be hard to repackage to test polyfills, I don't know)

I do not know where to submit that fix if is the good way to solve my problem

MDN is a wiki ;-) I need to understand the problem first. I'll come back to that.

@jareguo

This comment has been minimized.

Copy link

@jareguo jareguo commented Nov 5, 2015

Thanks 👍

@ohmyhusky

This comment has been minimized.

Copy link

@ohmyhusky ohmyhusky commented Feb 27, 2016

Still can not understand why should we check '&& oThis', is it means if we pass a null to bind like s = f.bind(null), then no matter use s as constructor -- new s,or normal function invoke -- s(), we always pass null to f.apply as f.apply(null,.....)? And then the null will be force change to global inside the function call f.apply. Why do me do this? Can you please help me out? thanks a lot in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment