Backbone.js入門 「Events」

### 閲覧上の注意

この記事で対象としているバージョン0.5.3は結構古いので注意してください。この記事でいえば、`bind`は無くなり、現在では`on`や`listenTo`が使われています。

その他の割りと新しい情報は [Backbone.js Advent Calendar 2012](http://www.adventar.org/calendars/15) などにあります。

（追記ここまで）

----

ネタ切れ感が否めないBackbone.js Advent Calendarですが、今回から何回かに分けて懇切丁寧な入門記事を書いていこうと思います。
以下のように書き進めていく予定です。

1. Events
2. View
3. Model
4. ViewとModelの連携
5. Collection
6. ViewとModelとCollectionの連携
7. RouterとHistory

なおここで扱うBackbone.jsのバージョンは0.5.3です。

#Backbone.js入門 「Events」

[Backbone.jsのドキュメント](http://documentcloud.github.com/backbone/)を開くとまず最初に解説されているのが `Events` です。
`Events` を直接使うことは無いかも知れません。それというのも `Model` `Collection` `Router` `View` は `Events` を拡張しており、しかも用途に特化した様々な機能を持っているため、これらを使った方が便利だからです。
しかしながら、上記のオブジェクトを用いる中で間接的に `Events` を利用しているということができます。
Backbone.jsでのプログラミングは、この `Events` が提供するインタフェースを用いて **[オブザーバ・パターン](http://ja.wikipedia.org/wiki/Observer_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3)** を上手く使い、オブジェクト間の疎結合を促進することが基本的なコーディング方針です。

##オブザーバ・パターン

JavaScriptに限らず、A.aが行われたら、B.aを行いたい、というような場面があります。

```js
A.a = function () {
    // do something
    B.a();
};
```

これで問題無いように思えますが、AはBがaメソッドを持っていることを知っていなければなりませんし、後になってC.aを追加したくなるかも知れません。それに、動的にB.aが実行されるかどうかを切り替えたい場合は、さらにゴチャゴチャしてきます。
このような場面でオブザーバ・パターンを用いることで、プログラムの見通しをよくすることができます。(Aのようなオブジェクトを *出版者* や *Subject* 、Bを *購読者* や *Observer* と呼びます)

```js
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` を用いて定義すると以下のようになります。

```js
A = {};
_.extend(A, Backbone.Events);
```

`Events` を拡張したAには`bind`, `unbind` そして `trigger` の３つのメソッドが追加されています。

###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* オブジェクトになります。つまり

```js
B = {
    name: "B",
    b: function () { console.log(this.name) };
}
A.name = "A";
A.bind("foo", B.b);
A.trigger("foo"); // => "A"
```

これは意図しない動作でしょう。ここで、`this` をBにする方法はいくつかあります。まずは、`context` を使う方法です

```js
A.bind("foo", B.b, B);
A.trigger("foo"); // => "B"
```

もう一つは、`_.bind` を使って事前に `this` を束縛する方法です。(紛らわしいですが、この `bind` は `Events#bind` とは全くの別物です)

```js
B.b = _.bind(B.b, B);
A.bind("foo", B.b);
A.trigger("foo"); // => "B"
```

この方法の利点は、金輪際、どのような状況で呼び出されようと、B.b() が "B" と出力するようになることです。B.b を何度もコールバック関数として使う場合は、先に `_.bind` しておくとよいでしょう。
しかしながら、Bのメソッドが増えてくると、いちいち `_.bind` を呼ぶのが面倒です。

```js
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回呼ぶ代わりに、次のように書くことができます。

```js
_.bindAll(B, "b1","b2","b3","b4","b5","b6","b7","b8","b9","b10");
```

##次回は

次回は「View」です。お楽しみに

---

1. Backbone.js入門 Events
2. [Backbone.js入門 MVC](http://qiita.com/items/03f49c64d6c4bacdbb26)
3. [Backbone.js入門 View](http://qiita.com/items/8ee2ed1949cd7c337f06)
4. [Backbone.js入門 Model](http://qiita.com/items/5acef8dd49f67fd7813c)
5. [Backbone.js入門 ViewとModelの連携](http://qiita.com/items/7ce4b1ceb5d18c9cef08)
6. [Backbone.js入門 Collection](http://qiita.com/items/c19d2fff0dc23e4015ff)
7. [Backbone.js入門 ViewとModelとCollectionの連携](http://qiita.com/items/7095a32cc5e438172844)
8. [Backbone.js入門 RouterとHistory](http://qiita.com/items/13f3d1f71d31f3e78123)

- [BackboneJS 日本語メーリングリスト](https://groups.google.com/forum/?fromgroups#!forum/backbonejs-ja)