问题
I understand this is a problem (or behaviour) of javascript itself rather than Backbone's extend method, but I'd like to know what is the best strategy to avoid it.
Let's better put it in 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 do understand that the problem is I'm not assigning a new value to mySecondModel.values I'm just operating on the values variable that is in the prototype, that is MyModel.prototype.values (same problem with any other object, of course)
But it's very easy to mess with that. The most intuitive thing is to just think of those as INSTANCE variables, and not variables common to every instance (static or class variables in class based languages).
So far now the general solution I've found is to initialize every variable in the initialize method, like this:
var MyModel = Backbone.Model.extend({
initialize: function() {
this.value = 0;
this.values = [];
}
});
That way everything works as expected, and even though it wouldn't be neccesary for a simple value (like this.value) I find it much easier to just stick to this prnciple in every case.
I'm wondering if there's some better (more elegant, clearer) solution to this problem
回答1:
This is an effect of JavaScript's prototypical inheritance and the fact that Array
objects are reference types. The key/value pairs of the object you pass to extend
are copied onto the prototype of MyModel
, so they will be shared by all instances of MyModel
. Because values
is an array, when you modify it, you modify the array for every instance.
What you are doing by setting values
inside initialize
is called shadowing the prototype, and it is the correct way to solve this issue.
That said, in the case of Backbone.Model
, if you are attempting to deal with the model's attributes, you can use the defaults
function to provide defaults like this:
var MyModel = Backbone.Model.extend({
defaults: function() {
return {
value: 0,
values: []
}
}
});
Again, this is only for attributes of an instance.
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 are doing, where you are specifying properties on the model itself, it is up to you to set the defaults inside of initialize
, as you have done.
回答2:
Are you intentionally not setting value
and values
as backbone attributes? If you set attributes on an instance, instead of putting them in the extended backbone model definition, it might work how 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 the console log.
回答3:
I too had stumbled across this problem some time back and solved it by defining a defaults method in the model.
var MyModel = Backbone.Model.extend({
defaults: function() {
return {
value: 0,
values: []
}
}
});
来源:https://stackoverflow.com/questions/14950479/backbone-annoying-behaviour-of-prototype-object