2016-07-12 20 views
4

Ich baue eine Middleware-Schicht zwischen meinen Socket.IO-Ereignissen und dem Rest meiner Anwendung. Ich tue das, damit ich Socket.IO für etwas anderes in der Zukunft austauschen kann.Wie zwei Funktionen verglichen werden, die mit `.bind()` aufgerufen werden?

Ich speichere Callback-Funktionen in einem Array. Wenn ein bestimmtes Ereignis ausgelöst wird, gehe ich über das Array und führe die Callback-Funktionen aus. Das funktioniert wie ein Zauber.

Das Problem liegt in der Entfernung eines Rückrufs von diesem Array. Wenn eine Callback-Funktion entfernt werden muss, führe ich eine Schleife über das Array und überprüfe jedes Array-Element, um zu sehen, ob es gleich ist (unter Verwendung von ===) mit dem Callback, den ich entfernen möchte. Wenn nur der Rückruf im Array gespeichert wird, funktioniert das problemlos. Wenn jedoch ein Rückruf in Kombination mit .bind() gespeichert wird, gibt die Gleichheitsüberprüfung false zurück.

Ich habe einen (vereinfachten) Codepen erstellt, um das Problem zu demonstrieren: http://codepen.io/petergoes/pen/wWPJdg?editors=0012.

Ich ziehe Inspiration aus dem Socket.IO-Code, um meine Entfernungsmethode zu schreiben: https://github.com/socketio/socket.io-client/blob/master/socket.io.js#L1623. Dort tritt jedoch das gleiche Problem auf.

Die große Frage:
Wie kann ich vergleichen zwei Funktionen, wenn (eine oder beide) mit der .bind() Methode aufgerufen?

Ich fand diese Antwort how do I compare 2 functions in javascript. Allerdings fühlt sich die String-Version einer Funktion vergleicht ein wenig lückenhaft

Die codepen Referenz:

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); 
+0

Ich denke, das funktioniert nicht mit 'bind' (außer mit jedem Hack wahrscheinlich). Sie sollten sich sowieso nicht auf Funktionsidentitäten oder Funktionsnamen verlassen. Diese Art von Metaprogrammierung führt (meiner Meinung nach) zu schlechtem Code. – ftor

+0

@ LUH3417 Was meinen Sie mit Funktionsidentitäten? –

+0

Gegeben ist 'const f = x => x + 1, g = x => x + 1' als der folgende Ausdruck vergleicht die Identität der Funktionsobjekte 'f === g', was 'falsch' ergibt. Daher sind sie gleich, aber nicht identisch. – ftor

Antwort

1

Ich denke, die beste Lösung hier ist, einen Schlüssel neben Ihrem Rückruf innerhalb des Arrays zu speichern. Das ist viel zuverlässiger und Weise weniger hacky als jede Lösung zu vergleichen Funktionen:

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

können Sie dann eher gewünscht wird, den Schlüssel entfernen aufrufen, indem als der Rückruf, und Sie können als solche vergleichen:

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

Stellen Sie sicher, dass Sie den gewünschten Schlüssel bei der Registrierung übergeben und auf die Callback-Eigenschaft innerhalb der Array-Objekte zugreifen, wo dies erforderlich ist.

Ich denke, diese Lösung mit Etiketten ist viel sauberer und erfordert keinen verwirrenden oder seltsamen Code.

+0

sollte es nicht 'callbackList.push (obj);' ?? – charlietfl

+0

ja! meine schlechte, bearbeitete – Pabs123

+0

@ Pabs123 Ich bin nicht ganz damit einverstanden, dass ein zusätzlicher Schlüssel weniger verwirrend ist. Betrachten wir die Socket.IO oder jQuery API zur Registrierung eines Ereignisses ('socket.on ('eventname', myFunction)', '$ ('. MyElement'). On ('eventname', myFunction)') ist (für mich) sauberer als 'myEvents.register ('eventName', myFunction, 'myKey')'. Oder vermisse ich deinen Standpunkt? (Ich weiß, dass mein anfängliches Beispiel das Argument '' eventName '' nicht enthält. Ich habe es aus Gründen der Übersichtlichkeit des Beispiels weggelassen.) –

-1

fand ich einen Verweis auf die gebundene Funktion Speicherung scheint zu funktionieren:

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

I Den Grund, warum Sie eine Referenz benötigen, ist, weil bind eine neue Funktion zurückgibt. Daher funktioniert der Vergleich in Ihrem Beispiel nicht, da es sich tatsächlich um zwei verschiedene Funktionen handelt.

+0

Nicht sicher, warum dies abgelehnt wird. Wenn es akzeptabel ist, zusätzliche Daten hinzuzufügen, um die gebundene Funktion zu verfolgen (wie in der akzeptierten Antwort), warum sollte man nicht einfach einen Verweis auf die gebundene Funktion verwenden? Beide Lösungen erfordern die Verfolgung der gebundenen Funktion, aber die Schlüssellösung erfordert die Zuordnung zusätzlicher unnötiger Daten. Darüber hinaus beantwortet diese Lösung tatsächlich die ursprüngliche Frage. Sie vergleichen gebundene Funktionen durch Vergleich ihrer Referenzen. –

1

Bedenken Sie:

function a(x) { return x; } 

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

a !== btrue ist, weil a.bind eine neue Funktion gibt, das ist ein neues Objekt, und das Objekt sind durch Referenz verglichen.

Der Hack:

Denken Sie daran, mit einheimischen Prototypen vermasselt nicht empfohlen, dies ist ein gemeiner Hack und die Tasten wie @ Pabs123 Verwendung könnte ein besserer Ansatz

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; 
    }; 
}()); 

Jetzt sein haben einen Verweis auf die Funktion, die gebunden ist, und Sie können, dass man überprüfen, wenn Vergleich:

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

Beachten Sie, dass th Der Hack muss implementiert werden, bevor eine Bindung ausgeführt wird. Alle Aufrufe an bind, bevor der Hack ausgeführt wird, führen nicht zur Ausgabe der original -Eigenschaft in der Ergebnisfunktion.


Ein simmilar Ansatz, aber ohne den Hack zu tun wäre, um Ihre benutzerdefinierte bind Methode zu tun, die den Verweis auf die ursprüngliche Funktion hält. Aber Sie müssen diese Methode anstelle des ursprünglichen bind verwenden, um den Verweis auf die ursprüngliche Funktion zu erhalten.

+1

das ist eine coole Lösung! – Pabs123

Verwandte Themen