Note that there are some explanatory texts on larger screens.

plurals
  1. POImplement javascript instance store by returning existing instance from constructor
    primarykey
    data
    text
    <p>I am trying to implement my version of the "Instance Store" in Backbone.js as described by Soundcloud in their recent blog post:</p> <p><a href="http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/" rel="noreferrer">http://backstage.soundcloud.com/2012/06/building-the-next-soundcloud/</a></p> <p>Relevant Excerpt:</p> <p>To solve this, we use a construct we call the instance store. This store is an object which is implicitly accessed and modified each time a constructor for a model is called. When a model is constructed for the first time, it injects itself into the store, using its id as a unique key. If the same model constructor is called with the same id, then the original instance is returned.</p> <pre><code>var s1 = new Sound({id: 123}), s2 = new Sound({id: 123}); s1 === s2; // true, these are the exact same object. </code></pre> <p>This works because of a surprisingly little-known feature of Javascript. If a constructor returns an object, then that is the value assigned. Therefore, if we return a reference to the instance created earlier, we get the desired behaviour. Behind the scenes, the constructor is basically doing this:</p> <pre><code>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; } </code></pre> <p>I implemented my version of this by overriding the Backbone.Model class to create my own constructor.</p> <pre><code>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 }); </code></pre> <p>This was working just fine, but something must have changed and now it's stopped working, and I'm unsure why. Newly created instances are stored in the store object with no issue - each class that extends the MyModel class has its own empty store to avoid collisions of instances of a different type with the same id. The correct instance is also retrieved with no issue when the constructor is called with an existing id, however when they are returned from the constructor the return value is ignored. My understanding from the spec is that constructors can return an object - but not a primitive - and the returned object will be assigned to the lefthand side of the assignment statement when the constructor is called with the new operator. This isn't happening, even though the constructor returns an object, the empty object created by the new operator is used.</p> <p>Some debugging info. Not sure how helpful this info will be. This is "this" in the MyModel constructor for an object being instantiated for the first time.</p> <pre><code>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 </code></pre> <p>And this is "this" in the MyModel constructor when it's an object being returned from the instance store:</p> <pre><code>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 </code></pre> <p>What I note is that the attributes object in the second one has all the methods of a backbone object included in there, which they shouldn't be. It also has no id, again I'm not sure why. Hopefully this provides some insight. Thanks.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload