Why do my javascript Backbone.js Models share the same "instance" of their parent class?
EDIT: Fixed my example开发者_运维技巧 to demonstrate the behavior.
I wanted the details
property of AbstractParent to be what I would call an "instance property" but it's acting more like a "static property". I realize these terms are not appropriate for javascript, so why when I create a new child class does it not get its own unique AbstractParent prototype? Why do they share the same one?
In the code below I expect an empty alert, but instead I get 'new details'
var AbstractParent = Backbone.Model.extend({
details: [],
addDetail: function(detail) {
this.details.push(detail);
},
getDetails: function() {
var rtn = '';
for(var i=0;i<this.details.length;i++) {
rtn += this.details[i];
}
return rtn;
}
});
var Child = AbstractParent.extend({});
var ch1 = new Child;
ch1.addDetail("new details");
var ch2 = new Child;
alert(ch2.getDetails()); // => 'new details'
This seems to only work this way when details
is an array. If it is a string or an object then it is not shared by ch1
and ch2
.
Okay, I figured out the issue. My original question's code was not accurate so I apologize to the other responders (they were correct in the context of the code I presented).
The solution is described here: Javascript object members that are prototyped as arrays become shared by all class instances
Basically, the fact that both Child instances share as their prototype the same AbstractParent function means that changing anything in AbstractParent will be reflected in both of the children's prototypes.
The reason this only manifests itself when using an array is because I initialized the array in the prototype and then just alter it rather than overwriting it in the addDetail
function. When I used objects or strings for the property names and then did an assignment, the this
in the context at the time of the function call belongs to the Child instance, so doing this.name = 'Billy';
in an AbstractParent function is in fact attaching that to the Child instance instead, and not in the AbstractParent. I was thinking each Child instance would get its own AbstractParent prototype instance and that when I accessed this.name it wouldn't find it on the Child and would go to this.prototype.name
instead.
In the code in my question I can fix this by adding an initialize
function to AbstractParent that instantiates the details array. But if I were to add an initialize
function to the Child and I did not make a call to the parent constructor in it then I would have the same issue as before.
You have assigned a name property (or object) to the base, AbstractParent, and you have defined two Objects which extend from this base. As such, each independently has this name object, though they default to the value assigned in the base.
In your code, though you have assigned ch1.name to 'Billy', you have done nothing to change the value of name for ch2. I contend that your code example is actually wrong. The value displayed by the alert should be 'Default Name'.
Could you load backbone.js in a browser, then go to the debugger console, and enter your above statements in again? If you do, I think you'll find the behavior you expect.
Are you saying when you change ch1.name, it also changes ch2.name? I ran your exact code and did not get that result. ch2.name was still "Default name".
When looking for the name property, my browser took the following routes after setting ch1.name = "Billy"...
ch1
inherits.child (your ch1 instance)
- name: "Billy"
ch2
- inherits.child (your ch2 instance)
- __proto__ (your SecondChild prototype)
- __proto__ (your AbstractParent prototype)
- name: "Default name"
- __proto__ (your AbstractParent prototype)
- __proto__ (your SecondChild prototype)
Open up a debug console, either Firebug in Firefox or Developer Tools / Javascript console in Chrome.
精彩评论