McShaman
McShaman

Reputation: 3995

Test if variable is a specific bound function in using javascript's Function.prototype.bind()

I am trying to work out how to test if a variable is an instance of a specific bound function. Consider the following example:

var func = function( arg ) {
    // code here
}

myFunc = func.bind( null, 'val' );

if( myFunc == func ) {
    console.log( true );
} else {
    console.log( false );
}

Unfortunately this results in false. Is there some sort of way of testing the variable to find out what function it is bound to?

Upvotes: 1

Views: 1751

Answers (5)

PHP Guru
PHP Guru

Reputation: 1566

You can use the instanceof operator as long as your bound function is a constructable function and the variable you are testing is an instance of your constructable.

From MDN:

When using a bound function as the right-hand side of instanceof, instanceof would reach for the target function (which is stored internally in the bound function) and read its prototype instead.

var func = function( arg ) {
    // code here
}

myFunc = func.bind( null, 'val' );

if( new func instanceof myFunc ) {
    console.log( true );
} else {
    console.log( false );
}

You simply have to write func so that it doesn't do anything unwanted when creating a new instance of it. For example if func doesn't need to construct:

var func = function( arg ) {
    if (this instanceof func) return;// do nothing
    // code here
}

Upvotes: 0

Joseph Garrone
Joseph Garrone

Reputation: 1772

Bind prepend "bound " before the source function's name.

If you can give a explicite name to the source function then you can do :

var func = function func( arg ) {
    // code here
}

var myFunc = func.bind( null, 'val' );


if( myFunc.name.match(/^(bound\ )*(.*)$/i)[2] === func.name ){

        console.log(true);

}

Upvotes: 1

McShaman
McShaman

Reputation: 3995

Thanks for your input @jfriend00 and @PaulS. I am using a function that automatically adds an unbound property to the bound function. Here is a refined version of what I wrote. Let me know what you think.

// My function
var myFunc = function() {
    return 'This is my function';
};

// Function I'm wrapping my function in
var wrapper = function( fn ) {

    var result;

    if( fn ) {
        result = fn.apply( null, arguments );
    }

    // Any additional logic I want to run after my function
    console.log( 'Ran wrapper logic' );

    return result;

};

// Modified binder method
var binder = function( fn, ths, args ) {

    args = [].concat( ths, args );

    var bound = fn.bind.apply( fn, args );

    bound.unbound = fn;

    return bound;

};

// Bind a wrapped version of my function 
myFunc = binder( wrapper, null, myFunc );

// I can test if my function has ben wrapped
console.log( myFunc.unbound == wrapper );

// And I can run a wrapped version of my function
console.log( myFunc() );

Upvotes: 0

jfriend00
jfriend00

Reputation: 707716

No, there is not a way to do this. .bind() returns a new function that internally calls the original one. There is no interface on that new function to retrieve the original one.

Per the ECMAScript specification 15.3.4.5, the returned "bound" function will have internal properties for [[TargetFunction]], [[BoundThis]] and [[BoundArgs]], but those properties are not public.

If you tell us what higher level problem you're trying to solve, we might be able to come up with a different type of solution.


If you yourself control the .bind() operation, you could put the original function on the bound function as a property and you could test that property:

var func = function( arg ) {
    // code here
}

myFunc = func.bind( null, 'val' );
myFunc.origFn = func;

if( myFunc === func || myFunc.origFn === func) {
    console.log( true );
} else {
    console.log( false );
}

Demo: http://jsfiddle.net/jfriend00/e2gq6n8y/

You could even make your own .bind() replacement that did this automatically.

function bind2(fn) {
    // make copy of args and remove the fn argument
    var args = Array.prototype.slice.call(arguments, 1);
    var b = fn.bind.apply(fn, args);
    b.origFn = fn;
    return b;
}

Upvotes: 7

Paul S.
Paul S.

Reputation: 66364

You can't do this directly because functions, just like Objects, have their equality tested by their reference which no longer matches, §11.9.3, point 1. f. or §11.9.6, point 7.

However, you could create some custom properties to test for, e.g.

function myBind(fn) { // takes 2+ args, the fn to bind, the new this, any other args
    var bind = Function.prototype.bind,
        bound = bind.call.apply(bind, arguments);
    bound.unbound = fn;
    return bound;
}

and then examle usage

function foo(bar) {
    console.log(this, bar);
}

// binding
var fizz = myBind(foo, {buzz:0}, 'baz');
fizz(); // logs {buzz: 0} "baz"

// testing
fizz.unbound === foo; // true

If you want to test in both directions, then you will need to OR them together, and perhaps even consider looping over these properties if you will be binding already bound functions

fizz.unbound === foo || fizz === foo.unbound; // true

Please also consider that the whole chain of unbound versions of the function will not be released from memory as long as a bound version exists, whereas some browsers would have been able to free this memory, depending on their implementation of bind

Upvotes: 2

Related Questions