I’m trying to use inheritance in JavaScript here, and I found problem having array value in Parent class that is inherited by a Child class. Below code is normal inheritance:
var Parent = function() {
this.list = [];
};
var Child = function() {};
Child.prototype = new Parent;
Child.prototype.constructor = Child;
var obj1 = new Child;
obj1.list.push("hello");
console.log(obj1.list); // prints ["hello"];
When I initialized new Child object (inherits Parent which contains array variable named list) to obj1 and tried to push obj1.list with a value “hello”, obj1.list prints [“hello”] .. so far so good.
The problem comes when I did the above example and I tried to initialize new Child object to obj2 then push obj2‘s list with a value “goodbye”, and obj2.list now prints [“hello”, “goodbye”]. (See the code below:)
var obj2 = new Child;
obj2.list.push("goodbye");
console.log(obj2.list); // prints ["hello", "goodbye"];
I might have a misconception here, but the list array in Parent is somehow retaining the value and I don’t know why.
This is a big trouble because if I reuse the Parent class to many other child classes like the case above, if Parent has array variable shared to its child classes, the value will be shared to the other child classes as well, which is unexpected for me.
What I expected is, the Child class represents new object, same goes to Parent class when Child class is initialized to obj1, which then when I initialize new Child object to obj2, the pushed value from obj1 should not be shared with obj2.
— Question —
Can anyone help me find out why the list (the array variable of Parent) in the example above retains the values/share the values that are initiated by the Child objects (in above case, obj1 and obj2)?
If you have another solution that could solve this problem, it will be very nice of you, but I’ll be happy to find out the problem above first.
When a child object has a property inherited from its prototype object, what’s really happening is that the child has a reference to the prototype, which contains the property. The child doesn’t have its own copy of it. So both children are using the same array — the one on the (one)
Parentprototype object you’ve assigned toChild.prototype.First some pictures, then more text. 🙂
new Parent()gives you this:…which you then assign to
Child.prototype.Then,
new Child()gives you this:+------------------+ | Child instance 1 | +------------------+ +-----------------+ | (prototype) |------->| Parent instance | +------------------+ +-----------------+ | list = [] | +-----------------+Doing
new Child()again gives you another one:+------------------+ +------------------+ | Child instance 1 | | Child instance 2 | +------------------+ +------------------+ | (prototype) |---+ | (prototype) |---+ +------------------+ | +------------------+ | | | | | +-----------------+ +-------------------------+---->| Parent instance | +-----------------+ | list = [] | +-----------------+So as you can see, all of the
Childinstances are sharing the sameParentinstance (their prototype), which has an array on it.When you say:
…here’s what the JavaScript engine does when it sees
obj.list:objhas its own property calledlist. If not, then:obj‘s prototype has its own property calledlist. If not, then:obj‘s prototype’s prototype has its own property calledlist.In your case, since
objdoesn’t have its ownlistproperty, the engine looks to its prototype (theParentinstance you assigned toChild.prototype), which in this case does have the property. So that one is used. It’s not copied to the child or anything, it’s used. And of course, since it’s an array, pushing something on the array actually pushes it onto the array.If you were to assign something to
obj.list, thenobjwould get its ownlistproperty, breaking the chain. So puttingthis.list = [];inChildwould give each child its own list.You’ll see this any time a prototype has an object reference on it, where the object is a type that can be modified (a “mutable” object). Arrays, Dates, plain objects (
{}), RegExps, etc., they all have state and they can all be modified, so you’d see it with them. (Stringinstances are immutable, so although this same thing happens, you don’t see any effect from it because the string cannot be changed.)With primitives, although you inherit them, you can’t change their state (you can only replace them with a new primitive with a different state), so you don’t see this same effect. So if
objinherits the propertyfoofrom its prototype, andfoois42,alert(obj.foo)will get the value from the prototype and show “42”. The only way to changefoois to sayobj.foo = 67or similar — which givesobjits own copy offoo, distinct from the prototype’s copy. (This is true even if you use things like++and--, e.g.++obj.foo; that really gets evaluated asobj.foo = obj.foo + 1).