如何比较用`.bind()`调用的两个函数?

时间:2016-07-12 14:57:58

标签: javascript

我正在我的Socket.IO事件和我的应用程序的其余部分之间构建一个中间件层。我这样做是为了让我可以在未来将Socket.IO换成其他东西。

我将回调函数存储在数组中。当特定事件触发时,我遍历数组并执行回调函数。这就像一个魅力。

问题在于从该阵列中删除回调。当需要删除回调函数时,我遍历数组并检查每个数组项以查看它是否与我想要删除的回调相等(使用===)。当回调存储在数组中时,这很好。但是,当回调与.bind()一起存储时,等号检查将返回false。

我创建了一个(简化的)codepen来演示问题:http://codepen.io/petergoes/pen/wWPJdg?editors=0012

我从Socket.IO代码中汲取灵感来编写我的删除方法:https://github.com/socketio/socket.io-client/blob/master/socket.io.js#L1623。但是,也会出现同样的问题。

最大的问题:
如何使用.bind()方法调用(一个或两个)函数时,可以比较两个函数?

我找到了这个答案how do I compare 2 functions in javascript。但是,比较一个函数的字符串版本感觉有点粗略

代码表供参考:

var callbackList = [];

var objA = {
    myCallbackFunction: function myCallbackFunction() {
        console.log('hello my name is:', this.myName);
    }
}

var objB = {
    register: function register(callback) {
        console.log('register callback');
        callbackList.push(callback);
    },

    remove: function remove(callback) {
        console.log('remove callback');
        if(callbackList[0] === callback) {
            console.log('callback found, splice it');
            callbackList.splice(0, 1);
        } else {
            console.log('callback NOT found!');
        }
        console.log('callbackList length:', callbackList.length);
    }
}

objB.register(objA.myCallbackFunction.bind({myName: 'Peter'}));
objB.remove(objA.myCallbackFunction.bind({myName: 'Peter'}));

console.log('\nreset callbackList\n');
callbackList = [];

objB.register(objA.myCallbackFunction);
objB.remove(objA.myCallbackFunction);

3 个答案:

答案 0 :(得分:1)

我认为这里最好的解决方案是在数组内部存储一个密钥和回调。与比较功能的任何解决方案相比,这更可靠,更简洁:

register: function register(callback, key) {
        console.log('register callback');
        var obj = {
           key: key,
           callback: callback
        }
        callbackList.push(obj);
    }

然后你可以通过传递所需的键而不是回调来调用remove,你可以这样比较:

if(callbackList[0].key === key)

只需确保在注册时传递所需的密钥,并在必要时访问数组对象中的回调属性。

我认为这种使用标签的解决方案更清晰,不需要任何令人困惑或奇怪的代码。

答案 1 :(得分:1)

考虑一下:

function a(x) { return x; }

var b = a.bind({ foo: 'foo' });

a !== btrue因为a.bind返回一个新函数,即一个新对象,并通过引用比较对象。

黑客

请记住搞乱本机原型不是建议,这是一个讨厌的黑客和使用密钥,因为@ Pabs123说可能是一个更好的方法

Function.prototype.bind = (function () {
    var originalBind = Function.prototype.bind;
    return function (obj) {
        var bound = originalBind.apply(this, Array.prototype.slice.call(arguments, 1));
        bound.original = this;

        return bound;
    };
}());

现在有一个已绑定函数的引用,你可以在比较时检查它:

a = function (x) { return x; };
b = a.bind({});
b.original === a; // true

请注意,必须在任何绑定完成之前实现hack,在执行hack之前对bind的任何调用都不会在结果函数上生成original属性。

一种类似的方法,但没有做黑客就是做你的自定义绑定方法,保持对原始函数的引用。但是您需要使用此方法而不是本机bind才能获得对原始函数的引用。

答案 2 :(得分:-1)

我发现存储对绑定函数的引用似乎有效:

    var callbackList = [];

    var objA = {
        myCallbackFunction: function myCallbackFunction() {
            console.log('hello my name is:', this.myName);
        }
    }

    var objB = {
        register: function register(callback) {
            console.log('register callback');
            callbackList.push(callback);
        },

        remove: function remove(callback) {
            console.log('callbackList.length before:', callbackList.length)
            var cleanList = [];
            callbackList.forEach(function(cb){
                if( cb !== callback ){
                    cleanList.push(cb);
                }
            })
            callbackList = cleanList;
            console.log('callbackList.length after:', callbackList.length)
        }
    }

    var f = objA.myCallbackFunction.bind({myName: 'Peter'});
    var f2 = objA.myCallbackFunction.bind({myName: 'Peter'});
    objB.register(f);
    objB.register(f2);
    objB.remove(f);

我认为你需要引用的原因是因为bind返回一个新函数。因此,比较在您的示例中不起作用,因为它们实际上是两个不同的函数。