Eric Fuller
Eric Fuller

Reputation: 159

JavaScript inheritance issue inconsistency (single instance of parent for all child objects...or not?)

I have been digging deeper and deeper into JavaScript in an effort to set up a sound, maintainable inheritance model for a project I'm working on. I have come across some fantastic resources, one of which points out an interesting issue arising from pointing the prototype of a "class" to a new instance of another "class". It uses this article to illustrate the point.

Specifically, the article points out that creating instances of "Cat" via the following code (pulled from the second article linked above) will result in each instance of "Cat" pointing to the same single instance of "Mammal"

function Mammal(name){ 
    this.name=name;
    this.offspring=[];
} 
Mammal.prototype.haveABaby=function(){ 
    var newBaby=new Mammal("Baby "+this.name);
    this.offspring.push(newBaby);
    return newBaby;
} 
Mammal.prototype.toString=function(){ 
    return '[Mammal "'+this.name+'"]';
}    
Cat.prototype = new Mammal();
Cat.prototype.constructor=Cat;

As the article that points this out (the first one linked above) shows, using this code results in the following:

var myPet = new Cat('Felix');
myPet.haveABaby();
var myPet2 = new Cat('Garfield');
myPet2.haveABaby();

console.log(myPet2.offspring); // returns ["","",...]

So, myPet2 has two elements in its offspring array. This isn't ideal as it has has only had one baby.

That all makes sense to me; however, what's confusing is, if you change this.offspring=[] to this.offspring=0 and then change this.offspring.push(newBaby) to this.offspring++, each new instance of "Cat" appears to have its offspring attribute set at 0.

var myPet = new Cat('Felix');
myPet.haveABaby();
var myPet2 = new Cat('Garfield');
myPet2.haveABaby();

console.log(myPet2.offspring); // returns 1

So, myPet2 has 1 offspring. This is what should happen.

Why does this appear to reset this.offspring each time, whereas the array/.push() approach does not? My initial thought is that it has something do with with an array being treated as an object and thus any 'offspring' property of the Mammal type will always refer to the same thing, whereas an integer is a primitive value that is created anew each time. Yes?

Either way, aren't these two outcomes at odds with one another as far as whether or not a new instance of Mammal is created for each new Cat?

Upvotes: 0

Views: 112

Answers (2)

slebetman
slebetman

Reputation: 113866

Each instance of Cat creates a new instance of an Object object that has prototype Mammal. Note that while it's true that there is only one instance of Mammal, the this in both Cats are separate instances of Cat, not Mammal. What is happening is not caused by the fact that both Cat share a Mammal prototype, but because arrays are references, not values.

Thus, while there are two Cats and both Cats this are really separate instances, they both share the same array offspring.

This illustrates what's happening without the confusion of objects, constructors and prototypes:

var a=[];
var b=a;

a.push('hello');

alert(b[0]); // alerts 'hello'.

Contrast this to things that are values in javascript (strings and numbers):

var a=0;
var b=a;

a++;

alert(b); // alerts 0

Reference types (arrays & objects) merely point to the underlying object. Thus when a variable with reference type is assigned to another variable they both share the same object (only the address is copied, not the object itself).

In contrast, value types (numbers & strings) get copied when assigned to another variable.


Additional answer

One subtle side effect of this is that you can break the shared offspring reference between the Cats by reassigning one of them to another array:

myPet.offspring = [new Mammal('Kitty'), new Mammal('Spot')];
alert(myPet2.offspring.length); // alerts 0
                                // Because the code above causes the
                                // `offspring` property of myPet to
                                // point to a different array.

Upvotes: 1

HMR
HMR

Reputation: 39260

Already answered this today, you can find it here:

https://stackoverflow.com/a/26337868/1641941

Those mdn articles don't look like Mozilla developer network articles I suggest using this one as it doesn't create an instance of Parent to set prototype part of inheritance and doesn't modify objects you don't own to achieve a simple task as inheritance:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fcreate

Upvotes: 0

Related Questions