Backbone.js で events とか defaults とかを親クラスから継承する方法

events とか default とは何か

Backbone.js のクラス定義において、特殊なパラメーターがいくつか存在している。たとえば eventsdefaults は、 Backbone.js のチュートリアル的文書ではまっさきに触れられている特殊なパラメーターだ。

例えば、 defaults は Model の特殊パラメーターで、指定しておくことでクラスをインスタンス化した際に、デフォルトの値として使われるようになる。

var Person = Backbone.Model.extend({
  defaults: {
    name: 'John Doe',
    age: 25
  }
});

var person = new Person({ age: 100 });
person.get('name'); // 'John Doe'
person.get('age');  // 100

この events とか defaults は、実はオブジェクトを返す関数に置き換えることもできる。

var Person = Backbone.Model.extend({
  defaults: function () {
    return {
      name: 'John Doe',
      age: 25
    }
  }
});

var person = new Person({ age: 100 });
person.get('name'); // 'John Doe'
person.get('age');  // 100

関数はクラスがインスタンス化される時(initialize メソッドが呼ばれる前)に実行される。ではこの「関数に置き換えることができる」という特性を何に使うかというと、たとえば、インスタンスごとのユニークな ID を割り振りたい時なんかに使える。下記の例は ID ではなく年齢をランダムにするコードだけど、雰囲気は伝わるかと思う。

var Person = Backbone.Model.extend({
  defaults: function () {
    return {
      name: 'John Doe',
      age: 20 + Math.floor(Math.random() * 30)
    }
  }
});

var person = new Person();
person.get('age');  // 20 以上 50 未満のランダムな整数

var person2 = new Person();
person2.get('age');  // 20 以上 50 未満のランダムな整数

直面する問題

ここからが本題。クラスの継承を使っている場合、これらの特殊パラメーターは、親が持つ同名のパラメーターをうまいこと継承してくれないという問題にぶち当たる。 Person を継承した SuperMan は、 Person のデフォルト値を継承して欲しい。

var SuperMan = Person.extend({
  defaults: {
    power: 530000
  }
});

var superMan = new SuperMan({ name: 'Clark Kent' });
superMan.get('name');  // 'Clark Kent'
superMan.get('age');   // undefined (!)
superMan.get('power'); // 25

このように、 age は継承されずに undefined となってしまう。そこで継承のやり方を考えた。

events とか defaults を継承するスニペット

こうです。

var SuperMan2 = Person.extend({
  defaults: function () {
    return _.defaults({
      power: 530000
    }, _.result(Person.prototype, 'defaults'));
  },
  say: function () {}
});

ここで使っている特殊な関数は、 _.defaults および .result の2つ。いずれも Underscore.js が持っているメソッドだ。 _.defaults はあるオブジェクトのデフォルト値を設定したいときに役に立つメソッド。_.defaults の公式ドキュメント_.result は指定したプロパティの示す値が関数ならば実行した返り値を、そうでなければそのまま返してくれるメソッド。_.result の公式ドキュメント。そんなに難しいことはしていないです。

こんなふうにして、特殊パラメーターを親クラスから継承しつつ、値を設定することができた。

使いどころ

そもそもこういう、「オブジェクトを指定するところだけど、実は関数も指定できる」箇所ってどこなの? という疑問が湧く。実際のところ、以下の部分で使える。

  • Model.defaults
  • Model.urlRoot
  • Model.url
  • Collection.url
  • View.events
  • View.options
  • View.attributes
  • View.id
  • View.className
  • View.tagName
  • View.el

こんな感じに出てきた。これらには、オブジェクトや文字列ばかりじゃなくて、関数が指定できるっていうことですね。知っておくと細かい融通が利かせられるようになりそう。

ここまでのまとめ