Soundcloud の最近のブログ投稿で説明されているように、Backbone.js に自分のバージョンの「インスタンス ストア」を実装しようとしています。
http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/
関連する抜粋:
これを解決するために、インスタンス ストアと呼ばれる構造を使用します。このストアは、モデルのコンストラクターが呼び出されるたびに暗黙的にアクセスおよび変更されるオブジェクトです。モデルが初めて構築されるとき、その ID を一意のキーとして使用して、ストアに自身を注入します。同じモデル コンストラクターが同じ ID で呼び出された場合、元のインスタンスが返されます。
var s1 = new Sound({id: 123}),
s2 = new Sound({id: 123});
s1 === s2; // true, these are the exact same object.
これが機能するのは、驚くほどあまり知られていない Javascript の機能によるものです。コンストラクターがオブジェクトを返す場合、それが割り当てられた値です。したがって、以前に作成したインスタンスへの参照を返すと、目的の動作が得られます。舞台裏では、コンストラクターは基本的にこれを行っています。
var store = {};
function Sound(attributes) {
var id = attributes.id;
// check if this model has already been created
if (store[id]) {
// if yes, return that
return store[id];
}
// otherwise, store this instance
store[id] = this;
}
Backbone.Model クラスをオーバーライドして独自のコンストラクターを作成することで、このバージョンを実装しました。
var MyModel = Backbone.Model.extend({
constructor: function (attributes, options) {
var id = attributes ? attributes.id : undefined;
if (this.store[id]) {
return this.store[id];
}
Backbone.Model.prototype.constructor.apply(this, arguments);
if (id) {
this.store[id] = this;
}
}
});
var MyOtherModel = MyModel.extend({
store: {},
//other model stuff
});
これは問題なく機能していましたが、何かが変わったに違いなく、現在は機能しなくなりました。その理由はわかりません。新しく作成されたインスタンスはストア オブジェクトに問題なく格納されます。MyModel クラスを拡張する各クラスには、同じ ID を持つ異なるタイプのインスタンスの衝突を避けるために、独自の空のストアがあります。コンストラクターが既存の ID で呼び出された場合も、正しいインスタンスが問題なく取得されますが、コンストラクターから返された場合、戻り値は無視されます。仕様からの私の理解では、コンストラクターはオブジェクトを返すことができますが、プリミティブは返すことができず、コンストラクターが new 演算子で呼び出されると、返されたオブジェクトが代入ステートメントの左側に割り当てられます。コンストラクターがオブジェクトを返しても、これは起こっていません。
いくつかのデバッグ情報。この情報がどれほど役立つかはわかりません。これは、初めてインスタンス化されるオブジェクトの MyModel コンストラクターの「this」です。
child
_callbacks: Object
_escapedAttributes: Object
_previousAttributes: Object
_setting: false
attributes: Object
id: "4fd6140032a6e522f10009ac"
manufacturer_id: "4f4135ae32a6e52a53000001"
name: "Tide"
uniqueName: "tide"
__proto__: Object
cid: "c50"
collection: child
id: "4fd6140032a6e522f10009ac"
__proto__: ctor
constructor: function (){ parent.apply(this, arguments); }
defaults: Object
store: Object
url: function () {
urlRoot: function () {
__proto__: ctor
これは、インスタンス ストアから返されるオブジェクトである場合の MyModel コンストラクターの "this" です。
child
_callbacks: Object
_escapedAttributes: Object
_previousAttributes: Object
_setting: false
attributes: Object
_validate: function (attrs, options) {
bind: function (events, callback, context) {
change: function (options) {
changedAttributes: function (diff) {
clear: function (options) {
clone: function () {
constructor: function (){ parent.apply(this, arguments); }
defaults: Object
destroy: function (options) {
escape: function (attr) {
fetch: function (options) {
get: function (attr) {
has: function (attr) {
hasChanged: function (attr) {
idAttribute: "id"
initialize: function (){}
isNew: function () {
isValid: function () {
manufacturer_id: 0
name: ""
off: function (events, callback, context) {
on: function (events, callback, context) {
parse: function (resp, xhr) {
previous: function (attr) {
previousAttributes: function () {
save: function (key, value, options) {
set: function (key, value, options) {
store: Object
toJSON: function () {
trigger: function (events) {
unbind: function (events, callback, context) {
unset: function (attr, options) {
url: function () {
urlRoot: function () {
__proto__: Object
cid: "c141"
__proto__: ctor
constructor: function (){ parent.apply(this, arguments); }
defaults: Object
store: Object
url: function () {
urlRoot: function () {
__proto__: ctor
2 番目の属性オブジェクトには、バックボーン オブジェクトのすべてのメソッドが含まれていることに注意してください。また、IDもありません。理由はわかりません。うまくいけば、これはいくつかの洞察を提供します。ありがとう。