ryanzec
ryanzec

Reputation: 28040

Wierd behavior in prototypal inheritance

I am relatively new to the concept of prototypal inheritance so maybe this is how prototypal inheritance should work or maybe this is just javascript but it seem like prototypal inheritance is only good primative types. For example I have the following code:

var leg = {
    type: null
};

var Animal = {
  traits: {},
  leg: Object.create(leg)
};

var lion = Object.create(Animal);
lion.traits.legs = 4;
lion.leg.type = 'left';

var bird = Object.create(Animal);
bird.traits.legs = 2;
bird.leg.type = 'right';

alert(lion.traits.legs) // shows 2???
alert(lion.leg.type) // shows right???

The last two lines show my confusion. Is this really how prototypal inheritance is supposed to work or is this just how javascript implemented it?

If this is really how prototypal inheritance is supposed to work, I really don't see how that type of inheritance would be useful is most cases. If I can only have primitive type, that seems very limited in creating complex objects as there are many cases where I have an object storing an instance of another object.

Upvotes: 1

Views: 108

Answers (2)

JacquesB
JacquesB

Reputation: 42669

Yes, this is how it is supposed to work. The properties traits and leg are mutable object which are shared among instances. The idea of prototypal inheritance is that you inherit all state from the prototype unless you explicitly override it. You don't override the fields trait and leg, so they are shared with the prototype.

This is different from class-based inheritance where state is never inherited, you have to initialize all state in the constructor.

You can have complex objects, but if each instance needs to have its own fresh instances of object fields, you have to create them after creating the instance, just as in a constructor.

Prototypal inheritance is useful (compared to class-based inheritance) because:

  1. You can save a lot of memory because you only allocate "slots" for fields which are different from the prototype. In class-based objects you always have to allocate room for all fields even though they never change from the default value.

  2. It is easy to inherit changes. E.g. if you for example have a "window" prototype in a UI framework, and a lot of concrete windows inheriting from this prototype, you can change color on the prototype and it is automatically inherited to all instances which haven't overridden it explicitly.

Upvotes: 0

Esailija
Esailija

Reputation: 140230

Don't inherit non-primitive state. Inherit methods. Make each object have their own state. State is what makes an object unique, if all your objects share the same state, there is no point.

var Animal = {
    method1: function() {},
    method2: function() {}
};

var lion = Object.create(Animal, {
    traits: {
        value: {}
    },
    leg: {
        value: {
            type: null
        }
    }
});

var bird = Object.create(Animal, {
    traits: {
        value: {}
    },
    leg: {
        value: {
            type: null
        }
    }
});

Btw, there is a much less verbose and better supported syntax for doing pretty much the same thing:

function Animal(traits, leg) {
    this.traits = traits;
    this.leg = leg;
}

var method = Animal.prototype;

method.method1 = function() {

};

method.method2 = function() {

};


var lion = new Animal({}, {type: null}),
    bird = new Animal({}, {type: null});

Upvotes: 2

Related Questions