2016-07-12 9 views
4

私のSocket.IOイベントと他のアプリケーションとの間にミドルウェアレイヤーを構築しています。私はこれを実行して、将来Socket.IOを他のものに交換することができます。`.bind()`で呼び出される2つの関数を比較するには?

私はコールバック関数を配列に格納します。特定のイベントが発生すると、配列をループしてコールバック関数を実行します。それは魅力のように機能します。

問題は、その配列からコールバックを削除することにあります。コールバック関数を削除する必要がある場合は、配列をループし、すべての配列項目を調べて、削除するコールバックと等しいかどうかを確認します(===を使用)。コールバックが配列に格納されている場合、これは正常に動作します。ただし、コールバックが.bind()と組み合わされて格納されている場合、等しいチェックはfalseを返します。

私は問題を示すために(簡略化した)コードを作成しました:http://codepen.io/petergoes/pen/wWPJdg?editors=0012

私はSocket.IOコードからインスピレーションを得て、削除方法を書いています:​​。ただし、同じ問題が発生します。

大きな問題:
(1またはその両方)が.bind()メソッドで呼び出されたときに、私は2つの機能を比較するにはどうすればよいですか?

この回答が見つかりましたhow do I compare 2 functions in javascriptです。ただし、関数の文字列バージョンを比較すると、少し大ざっぱな感じ

codepen参考のために:

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

これは 'bind'では(おそらくハックを除いて)動作しません。とにかく関数のIDや関数名に頼るべきではありません。この種のメタプログラミングは、悪いコードにつながります(私の意見では)。 – ftor

+0

@ LUH3417機能IDはどういう意味ですか? –

+0

次の式より 'const f = x => x + 1、g = x => x + 1'とすると、関数オブジェクト' f === g'の同一性を比較して 'false'を返します。したがって、それらは同等ですが、同一ではありません。 – ftor

答えて

1

ここでの最適な解決策は、配列内にコールバックの横にキーを格納することです。

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

あなたは、コールバックではなく、目的のキーを渡すことで削除を呼び出すことができ、あなたのような比較することができます:

if(callbackList[0].key === key) 
これは、比較機能、より多くの信頼性が高く、どのソリューションよりも道少ないハックです

必要に応じて、配列オブジェクト内のコールバックプロパティに登録してアクセスするときに、目的のキーを渡すようにしてください。

私はこのラベルを使用したソリューションははるかにクリーンで、混乱したり奇妙なコードを必要としないと思います。

+0

は 'callbackList.push(obj);'ではないはずです。 – charlietfl

+0

はい!私の悪い、編集 – Pabs123

+0

@ Pabs123私は余分なキーがそれほど混乱しないことにかなり同意しない。イベント( 'eventname'、myFunction)、$( 'myElement')。on( 'eventname'、myFunction) ')を登録するためのSocket.IOまたはjQuery APIを見ると、(私にとっては) 'myEvents.register( 'eventName'、myFunction、 'myKey')'よりもきれいです。またはあなたのポイントを逃していますか? (私の初期の例には '' eventName''引数がないことが分かりますが、例を分かりやすくするために省略しています) –

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

I bindが新しい関数を返すため、参照が必要な理由が考えられます。したがって、実際には2つの異なる機能であるため、比較はあなたの例では機能しません。

+0

なぜこれがダウン投票されたのか分かりません。結合された関数を追跡するためにデータを追加することが許容される場合(受け入れられた回答の場合)、結合された関数への参照を使用するだけではどうですか?どちらのソリューションもバインドされた機能を追跡する必要がありますが、キーソリューションでは不要なデータを追加する必要があります。さらに、このソリューションは実際に元の質問に答えます。結合された関数は、それらの参照を比較することによって比較されます。 –

1

は、これを考慮してくださいa.bind新しい関数を返すため

function a(x) { return x; } 

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

a !== btrueがあり、それは新しいオブジェクトであり、オブジェクトが参照することにより比較されます。

ハック

は、ネイティブのプロトタイプをめちゃくちゃに心に留めておくがお奨めされていない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 

バインディングが行われる前にハックを実装しなければならない場合、ハックが実行される前にbindを呼び出すと結果関数にoriginalプロパティが生成されません。


類似のアプローチですが、ハックを行わずに、元の関数への参照を保持するカスタムバインドメソッドを実行することです。しかし元の関数への参照を得るためには、ネイティブbindの代わりにこのメソッドを使用する必要があります。

+1

それはクールな解決策です! – Pabs123

関連する問題