Determine if a JavaScript function is a bound function

前端 未结 7 1541
梦如初夏
梦如初夏 2020-12-14 16:51

Is there a way to determine if a JavaScript function is a bound function?

Example:

相关标签:
7条回答
  • 2020-12-14 17:05

    There is a module that can help you solve this problem : bind2.

    Here's a use case :

    const bind2 = require('bind2');
    
    function testFunc() {
      return this.hello;
    }
    const context = { hello: 'world' };
    
    const boundFunc = bind2(testFunc, context);
    console.log(boundFunc.bound); // true
    

    Full disclosure : I wrote this module.

    0 讨论(0)
  • 2020-12-14 17:06

    Both bound functions and arrow functions do not have a prototype property:

    typeof (function() {}).prototype // 'object' as usual
    typeof (function() {}).bind(null).prototype // 'undefined'!
    typeof (() => {}).prototype // 'undefined'!
    

    This is not 100% safe since you could still manually assign this property (although that'd be weird).
    As such, a simple way to check for bindability would be the following:

    // ES5
    function isBindable(func) {
      return func.hasOwnProperty('prototype');
    }
    
    // ES6
    const isBindable = func => func.hasOwnProperty('prototype');
    

    Usage:

    isBindable(function () {}); // true
    isBindable(() => {}); // false
    isBindable(
      (function () {}).bind(null)
    ); // false
    

    This way you can make sure that the function that has been passed can deal with a dynamic this.

    Here is an example usage for which the above fails:

    const arrowFunc = () => {};
    arrowFunc.prototype = 42;
    
    isBindable(arrowFunc); // true :(
    

    Interestingly, while bound functions do not have a prototype property they can still be used as constructors (with new):

    var Animal = function(name) {
       this.name = name;
    };
    
    Animal.prototype.getName = function() {
      return this.name;
    };
    
    var squirrel = new Animal('squirrel');
    console.log(squirrel.getName()); // prints "squirrel"
    
    var MutatedAnimal = Animal.bind({}); // Radiation :)
    console.log(MutatedAnimal.hasOwnProperty('prototype')); // prints "false"
    
    var mutatedSquirrel = new MutatedAnimal('squirrel with two heads');
    console.log(mutatedSquirrel.getName()); // prints "squirrel with two heads"
    

    In that case, the original function prototype (Animal) is used instead.
    See JS Bin, code and link courtesy of Dmitri Pavlutin.

    This of course won't work with arrow functions since they can't be used as constructors.

    Unfortunately, I don't know if there is a way to distinguish a bound function (usable as constructor) from an arrow function (not usable as constructor) without trying them out with new and checking if it throws (new (() => {}) throws a "is not a constructor" error).

    0 讨论(0)
  • 2020-12-14 17:06

    I'm new here and don't have enough reputation to post comments, just answers. Sorry, but this does not answer the OP, because I have no clue how to do so. I wish I did.

    However, the very common answers relying on a missing prototype property won't work. Class methods, method definitions in objects, and async functions also don't have prototype properties, but they are not naturally bound.

    Also, bear in mind that it's still possible to manually bind a function by closure, which would defy any attempts to detect its bound state:

    const bind=(fn,obj)=>{
        return (...args)=>{
            return fn.apply(obj,args)
        }
    }
    
    0 讨论(0)
  • 2020-12-14 17:09

    In environments that support ES6, you can check whether the name of the function starts with "bound " (the word "bound" followed by a space).

    From the spec:

    19.2.3.2 Function.prototype.bind ( thisArg , ...args)

    [...]

    15. Perform SetFunctionName(F, targetName, "bound").

    Of course that could result in false positives if the name of the function was manually changed.

    0 讨论(0)
  • 2020-12-14 17:10

    You would need to write your own bind function on the prototype. That function would build an index of what has been bound.

    You could then have another function to perform a lookup against the object where that index is stored.

    0 讨论(0)
  • 2020-12-14 17:13

    One could override the existing prototype bind, tagging functions that have been bound.

    A simple solution. This will likely kill certain optimizations in V8 (and possibly other runtimes) because of hidden classes, though.

    (function (bind) {
      Object.defineProperties(Function.prototype, {
        'bind': {
          value: function (context) {
            var newf = bind.apply(this, arguments);
            newf.context = context;
    
            return newf;
          }
        },
        'isBound': {
          value: function () {
            return this.hasOwnProperty('context');
          }
        }
      });
    }(Function.prototype.bind));

    In motion:

    (function (bind) {
      Object.defineProperties(Function.prototype, {
        'bind': {
          value: function (context) {
            var newf = bind.apply(this, arguments);
            newf.context = context;
    
            return newf;
          }
        },
        'isBound': {
          value: function () {
            return this.hasOwnProperty('context');
          }
        }
      });
    }(Function.prototype.bind));
    
    var a = function () {
      console.log(this);
    };
    var b = {
      b: true
    };
    var c = a.bind(b);
    
    console.log(a.isBound())
    console.log(c.isBound())
    console.log(c.context === b);
    a();
    c();

    0 讨论(0)
提交回复
热议问题