Backbone: annoying object prototype behavior

I understand this is a problem (or behavior) in the javascript itself and not the Backbone extend method, but I would like to know what is the best strategy to avoid this.

Better to put it in the code:

var MyModel = Backbone.Model.extend({
  value: 0,
  values: []
});

var myFirstModel = new MyModel();

myFirstModel.value // 0, as expected
myFirstModel.values // [], as expected

var mySecondModel = new MyModel();

mySecondModel.value = 2;
mySecondModel.values.push(2)

mySecondModel.value // 2, as expected
mySecondModel.values // [2], as expected

myFirstModel.value // 0, as expected
myFirstModel.values // [2], ... WAT!!!

      

I understand that the problem is that I am not assigning a new value to mySecondModel.values. I am just working on a value variable that is in the prototype i.e. MyModel.prototype.values ​​(same problem with any other object of course)

But this is easy to deal with. Most intuitively, these are just INSTANCE variables, not variables common to each instance (static or class variables in class-based languages).

So far, the general solution I have found is to initialize each variable in the initialize method, for example:

var MyModel = Backbone.Model.extend({
  initialize: function() {
    this.value = 0;
    this.values = [];
  }
});

      

This way everything works as expected, and while it's not necessary for a simple value (like this.value), it's much easier for me to just stick to this prnciple in every case.

I am wondering if there is a better (more elegant, clear) solution to this problem

+3


source to share


3 answers


This is the effect of JavaScript prototypical inheritance and the fact that objects Array

are reference types. The key / value pair of the object you pass to extend

is copied on the prototype MyModel

, so they will be used by all instances MyModel

. Since it values

is an array, when you change it, you change the array for each instance.

What you are doing while setting values

internally initialize

is called prototype shading and this is the right way to solve this problem.

In case Backbone.Model

you're trying to figure out the attributes of a model, you can use a function defaults

to provide defaults like this:

var MyModel = Backbone.Model.extend({
  defaults: function() {
    return {
      value: 0,
      values: []
    }
  }
});

      



Again, this is only for instance attributes.

var inst = new MyModel();

// The defaults will be created for each new model,
// so this will always return a new array.
var values = inst.get('values');

      

For what you do when you specify the properties of the model itself, you need to set default values ​​internally initialize

as you did.

+2


source


You do not deliberately set value

, and values

as basic attributes? If you set attributes on an instance instead of putting them in the extended base model definition, it might work as you expect.

var MyModel = Backbone.Model.extend();

var myFirstModel = new MyModel({
  value: 0,
  values: []
});

console.log(myFirstModel.get('value');  // 0
console.log(myFirstModel.get('values'); // []

var mySecondModel = new MyModel({
  value: 2,
  values: [2]
});

//mySecondModel.value = 2;
//mySecondModel.values.push(2)

console.log(mySecondModel.get('value');  // 2
console.log(mySecondModel.get('values'); // [2]

console.log(myFirstModel.get('value');   // 0
console.log(myFirstModel.get('values');  // []

      



jsFiddle , check your console log.

+2


source


I also came across this problem a few times and solved it by defining a defaults method in the model.

var MyModel = Backbone.Model.extend({
  defaults: function() {
    return {
      value: 0,
      values: []
    }
  }
});

      

+1


source







All Articles