Backbone.js
AdventCalendar

Backbone.js入門 「Events」

More than 3 years have passed since last update.

閲覧上の注意

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

その他の割りと新しい情報は Backbone.js Advent Calendar 2012 などにあります。

(追記ここまで)


ネタ切れ感が否めない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のドキュメントを開くとまず最初に解説されているのが Events です。
Events を直接使うことは無いかも知れません。それというのも Model Collection Router ViewEvents を拡張しており、しかも用途に特化した様々な機能を持っているため、これらを使った方が便利だからです。
しかしながら、上記のオブジェクトを用いる中で間接的に 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 はオプションの引数で、callbackthis に束縛されます。context は後述する Eventsを用いる時の注意点 でもう一度扱います。

unbind([eventName], [callback])

他のオブザーバ・パターンの実装では unregister と呼ばれることもあるメソッドで、既に束縛( bind )されているコールバック関数を解除することができます。eventNamecallback は両方省略可能で、両方省略すると全てのコールバック関数が解除されます。

trigger(eventName, [*args])

他のオブザーバ・パターンの実装では notify と呼ばれることもあるメソッドで、束縛されているコールバック関数を起動します。第二引数以降はそれぞれコールバック関数に引数として渡されます。

ここで、eventName"all" だった場合、このオブジェクトに束縛されている全てのコールバック関数が実行されます。

Eventsを用いる時の注意点

Events に限らず、コールバック関数内では this が何を指すのか気をつける必要があります。
bind において、context を指定しなかった場合、コールバック関数内で thisSubject オブジェクトになります。つまり

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 を束縛する方法です。(紛らわしいですが、この bindEvents#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」です。お楽しみに


  1. Backbone.js入門 Events
  2. Backbone.js入門 MVC
  3. Backbone.js入門 View
  4. Backbone.js入門 Model
  5. Backbone.js入門 ViewとModelの連携
  6. Backbone.js入門 Collection
  7. Backbone.js入門 ViewとModelとCollectionの連携
  8. Backbone.js入門 RouterとHistory