Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if a JavaScript function is a bound function

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

Example:

var obj = {    x:1    };    function printX() {      document.write(this.x);  }    function takesACallback(cb) {    // how can one determine if this is a bounded function    // not just a function?    if (typeof cb === 'function') {      cb();      }  }    takesACallback(printX.bind(obj)); // 1  takesACallback(printX);           // undefined

Perhaps this is an important point. I am not asking why the second call prints undefined.

like image 313
robbmj Avatar asked Feb 28 '16 18:02

robbmj


People also ask

What is bound function in JavaScript?

JavaScript Function bind() With the bind() method, an object can borrow a method from another object. The example below creates 2 objects (person and member).

How do you call a bound function in JavaScript?

You can use call() / apply() to invoke the function immediately. bind() returns a bound function that, when executed later, will have the correct context ("this") for calling the original function. So bind() can be used when the function needs to be called later in certain events when it's useful.

What is function bound?

If f is real-valued and f(x) ≤ A for all x in X, then the function is said to be bounded (from) above by A. If f(x) ≥ B for all x in X, then the function is said to be bounded (from) below by B. A real-valued function is bounded if and only if it is bounded from above and below.


2 Answers

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).

like image 197
Kyll Avatar answered Sep 21 '22 08:09

Kyll


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.

like image 21
Felix Kling Avatar answered Sep 21 '22 08:09

Felix Kling