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
source to share
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.
source to share
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.
source to share