Backbone.jsのbindおよびbindAllを理解する

Backbone.jsユーザーは、Underscore.jsライブラリが提供するbindメソッドとbindAllメソッドをよく使用します。 このブログでは、これらの方法が必要な理由とその仕組みについて説明します。



すべては適用から始まります



bindAll関数は、内部的にバインドを使用します。 そして、バインドは順番に適用を使用します。 したがって、applyの機能を理解することが重要です。



var func = function beautiful(){ alert(this + ' is beautiful'); }; func();
      
      





上記のコードを実行すると、「[object window] is beautiful」と表示されます。 このメッセージが表示されるのは、関数が呼び出されたときに、これがデフォルトのグローバルオブジェクトであるwindowと等しいためです。



この値を変更するには、以下に示すようにapplyメソッドを使用できます。



 var func = function beautiful(){ alert(this + ' is beautiful'); }; func.apply('Internet');
      
      





上記の場合、「Internet is beautiful」というメッセージが表示されます。 同様に、次のコードは「Beach is beautiful」を生成します。



 var func = function beautiful(){ alert(this + ' is beautiful'); }; func.apply('Beach'); //Beach is beautiful
      
      





要するに、関数を呼び出すときに、applyを使用してこの値を制御できます。



バインドが必要な理由



バインドメソッドが必要な理由を理解するために、最初に次の例を見てみましょう。



 function Developer(skill) { this.skill = skill; this.says = function(){ alert(this.skill + ' rocks!'); } } var john = new Developer('Ruby'); john.says(); //Ruby rocks!
      
      





上記の例は非常に単純です。 johnオブジェクトはDeveloperのインスタンスであり、says関数が呼び出されると、正しい警告メッセージが表示されます。



sayを呼び出すとき、次のようにjohn.says()を呼び出すことに注意してください。 戻り値を返す関数を取得したいだけであれば、john.saysを実行する必要があります。 したがって、上記のコードは、次の概要によって破られる可能性があります。



 function Developer(skill) { this.skill = skill; this.says = function(){ alert(this.skill + ' rocks!'); } } var john = new Developer('Ruby'); var func = john.says; func();// undefined rocks!
      
      





上記のコードは、上のコードに似ています。 関数をfuncという変数に保存するだけでした。 この関数を呼び出すと、期待するメッセージを取得するはずです。 ただし、このコードを実行すると、警告メッセージは「undefined rocks!」になります。



この場合、func関数はグローバルコンテキストで呼び出されたため、 "undefined rocks!"になります。 関数が実行されると、これはwindowというグローバルオブジェクトを指します。 また、ウィンドウオブジェクトには、skillというプロパティがありません。 したがって、this.skillの出力値は未定義です。



先ほど、applyを使用すると、これから生じる問題を解決できることがわかりました。 それでは、applyを使用して解決してみましょう。



 function Developer(skill) { this.skill = skill; this.says = function(){ alert(this.skill + ' rocks!'); } } var john = new Developer('Ruby'); var func = john.says; func.apply(john);
      
      





上記のコードは問題を解決します。 今回、受け取った通知メッセージは「Ruby rocks!」でした。 ただし、問題があり、かなり大きな問題があります。



JavaScriptの世界では、関数はファーストクラスオブジェクトです。 関数を作成する理由は、どこにでも簡単に渡すことができるためです。 上記のケースでは、funcという関数を作成しました。 ただし、func関数と共に、john変数をどこにでも渡す必要があります。 これは良い考えではありません。 第二に、この関数の正しい呼び出しに対する責任は、作成者関数から消費者関数に移されました。 これはあまり良いAPIではありません。



消費者が簡単に呼び出すことができる関数を作成する必要があります。 そして、ここでバインドが作用します。



バインドが問題を解決する方法



まず、バインドを使用して問題を解決する方法を見てみましょう。



 function Developer(skill) { this.skill = skill; this.says = function(){ alert(this.skill + ' rocks!'); } } var john = new Developer('Ruby'); var func = _.bind(john.says, john); func();// Ruby rocks!
      
      





これに関する問題を解決するには、すべての場所でそれを処理する必要がないような方法ですでにjohnにマップされている関数が必要です。 これは、まさにbindメソッドが行うことです。 新しい関数が返され、そのthis値が提供された値になります。



bindメソッドのコードの一部を次に示します。



 return function() { return func.apply(obj, args.concat(slice.call(arguments))); };
      
      





ご覧のとおり、bindはapplyを使用して、bindを呼び出したときに渡した2番目のパラメーターにこれを設定します。



bindは既存の関数を変更しないことに注意してください。 新しい関数が返されるため、使用する必要があります。



bindAllが問題を解決する方法



バインドの代わりに、bindAllを使用することもできます。 bindAllを使用したソリューションを次に示します。



 function Developer(skill) { this.skill = skill; this.says = function(){ alert(this.skill + ' rocks!'); } } var john = new Developer('Ruby'); _.bindAll(john, 'says'); var func = john.says; func(); //Ruby rocks!
      
      





上記のコードはバインドを使用したソリューションに似ていますが、いくつかの大きな違いがあります。 最初の違いは、bindAllの戻り値を心配する必要がないことです。 バインドの場合、return関数を使用する必要があります。 bindAllでは、戻り値については心配しませんが、代金を支払う必要があります。 実際、bindAllは関数を変更します。 これはどういう意味ですか?



johnオブジェクトには、関数を返すsayと呼ばれるプロパティがあることを確認してください。 bindAllメソッドは、関数を返すときにjohnオブジェクトに既に関連付けられているように、saysプロパティを変更します。



bindAllメソッドのコードスニペットを次に示します。



 function(f) { obj[f] = _.bind(obj[f], obj); }
      
      





bindAllは内部的にbindメソッドを呼び出し、既存のプロパティの値をbindによって返される関数に置き換えることに注意してください。



bindとbindAllのもう1つの違いは、バインドでは、最初のパラメーターがjohn.says関数であり、2番目がjohnオブジェクトであることです。 bindAllでは、最初のパラメーターはjohnオブジェクトであり、2番目のパラメーターは関数ではなく、プロパティの名前です。



何を探す



Backbone.jsアプリケーションを開発する際、誰かが次のようにコードを作成しました。



 window.ProductView = Backbone.View.extend({ initialize: function() { _.bind(this.render, this); this.model.bind('change', this.render); } });
      
      





バインドの戻り値が使用されないため、上記のコードは機能しません。 適切な使用方法は次のとおりです。



 window.ProductView = Backbone.View.extend({ initialize: function() { this.model.bind('change', _.bind(this.render, this)); } });
      
      





または、以下に示すようにbindAllを使用できます。



 window.ProductView = Backbone.View.extend({ initialize: function() { _.bindAll(this, this.render); this.model.bind('change', this.render); } });
      
      






All Articles