閲覧上の注意
この記事で対象としているバージョン0.5.3は結構古いので注意してください。この記事でいえば、bind
は無くなり、現在ではon
やlistenTo
が使われています。
その他の割りと新しい情報は Backbone.js Advent Calendar 2012 などにあります。
(追記ここまで)
ネタ切れ感が否めないBackbone.js Advent Calendarですが、今回から何回かに分けて懇切丁寧な入門記事を書いていこうと思います。
以下のように書き進めていく予定です。
- Events
- View
- Model
- ViewとModelの連携
- Collection
- ViewとModelとCollectionの連携
- RouterとHistory
なおここで扱うBackbone.jsのバージョンは0.5.3です。
#Backbone.js入門 「Events」
Backbone.jsのドキュメントを開くとまず最初に解説されているのが Events
です。
Events
を直接使うことは無いかも知れません。それというのも Model
Collection
Router
View
は Events
を拡張しており、しかも用途に特化した様々な機能を持っているため、これらを使った方が便利だからです。
しかしながら、上記のオブジェクトを用いる中で間接的に Events
を利用しているということができます。
Backbone.jsでのプログラミングは、この Events
が提供するインタフェースを用いて オブザーバ・パターン を上手く使い、オブジェクト間の疎結合を促進することが基本的なコーディング方針です。
##オブザーバ・パターン
JavaScriptに限らず、A.aが行われたら、B.aを行いたい、というような場面があります。
A.a = function () {
// do something
B.a();
};
これで問題無いように思えますが、AはBがaメソッドを持っていることを知っていなければなりませんし、後になってC.aを追加したくなるかも知れません。それに、動的にB.aが実行されるかどうかを切り替えたい場合は、さらにゴチャゴチャしてきます。
このような場面でオブザーバ・パターンを用いることで、プログラムの見通しをよくすることができます。(Aのようなオブジェクトを 出版者 や Subject 、Bを 購読者 や Observer と呼びます)
A.bind("doA", B.a); // A に対して doA が trigger されたら B.a を実行する
A.a = function () {
// do something
this.trigger("doA") // => B.a が実行される
}
あとからC.aも実行したくなったら A.bind("doA", C.a)
を実行すればよい訳です。
##Eventsの使い方
ここでAを Events
を用いて定義すると以下のようになります。
A = {};
_.extend(A, Backbone.Events);
Events
を拡張したAにはbind
, unbind
そして trigger
の3つのメソッドが追加されています。
###bind(eventName, callback, [context])
他のオブザーバ・パターンの実装では register と呼ばれることもあるメソッドで、A.trigger(eventName)
によって callback
が起動されるようになります。context
はオプションの引数で、callback
の this
に束縛されます。context
は後述する Eventsを用いる時の注意点 でもう一度扱います。
###unbind([eventName], [callback])
他のオブザーバ・パターンの実装では unregister と呼ばれることもあるメソッドで、既に束縛( bind )されているコールバック関数を解除することができます。eventName
と callback
は両方省略可能で、両方省略すると全てのコールバック関数が解除されます。
###trigger(eventName, [*args])
他のオブザーバ・パターンの実装では notify と呼ばれることもあるメソッドで、束縛されているコールバック関数を起動します。第二引数以降はそれぞれコールバック関数に引数として渡されます。
ここで、eventName
が "all" だった場合、このオブジェクトに束縛されている全てのコールバック関数が実行されます。
##Eventsを用いる時の注意点
Events
に限らず、コールバック関数内では this
が何を指すのか気をつける必要があります。
bind
において、context
を指定しなかった場合、コールバック関数内で this
は Subject オブジェクトになります。つまり
B = {
name: "B",
b: function () { console.log(this.name) };
}
A.name = "A";
A.bind("foo", B.b);
A.trigger("foo"); // => "A"
これは意図しない動作でしょう。ここで、this
をBにする方法はいくつかあります。まずは、context
を使う方法です
A.bind("foo", B.b, B);
A.trigger("foo"); // => "B"
もう一つは、_.bind
を使って事前に this
を束縛する方法です。(紛らわしいですが、この bind
は Events#bind
とは全くの別物です)
B.b = _.bind(B.b, B);
A.bind("foo", B.b);
A.trigger("foo"); // => "B"
この方法の利点は、金輪際、どのような状況で呼び出されようと、B.b() が "B" と出力するようになることです。B.b を何度もコールバック関数として使う場合は、先に _.bind
しておくとよいでしょう。
しかしながら、Bのメソッドが増えてくると、いちいち _.bind
を呼ぶのが面倒です。
B = {
b1: function () {},
b2: function () {},
b3: function () {},
b4: function () {},
b5: function () {},
b6: function () {},
b7: function () {},
b8: function () {},
b9: function () {},
b10: function () {}
};
B.b1 = _.bind(B.b1, B);
// ... 面倒
B.b10 = _.bind(B.b10, B);
こういう場合は _.bindAll
が便利です。_.bind
を10回呼ぶ代わりに、次のように書くことができます。
_.bindAll(B, "b1","b2","b3","b4","b5","b6","b7","b8","b9","b10");
##次回は
次回は「View」です。お楽しみに