Search code examples
javascriptinheritanceobject-literal

object literal used in inherited property changes all instances of the class


I have the following code (ObjA) and it works as I would expect, instance 'a1' has the same properties as 'a2' but with different values.

function ObjA() {
    this.objLiteral = {};
    this.propA = 0;
}

var a1 = new ObjA();
a1.objLiteral['hello'] = 3;
a1.propA = 1.5;

var a2 = new ObjA();
a2.objLiteral['goodbye'] = 4;
a2.propA = 2;

debugger info for a1 and a2:
http://www.flickr.com/photos/76617756@N02/6879283032/

Next, I have the following ObjB that inherits from ObjA. Instance 'b1' and 'b2' have the same properties and different values for properties propA and propB but for some reason, objLiteral is the same in both as if it was referencing the same object.

ObjB.prototype = new ObjA();
ObjB.prototype.constructor=ObjB;

function ObjB() {
    this.propB = 2;
}

var b1 = new ObjB();
b1.objLiteral['hello2'] = 6;
b1.propA = 4;
b1.propB = 5;

var b2 = new ObjB();
b2.objLiteral['goodbye2'] = 8;
b2.propA = 6;
b2.propB = 7;

debugger info for b1 and b2:
http://www.flickr.com/photos/76617756@N02/6879283088/

Can somebody help me understand what is happening? What do I have to do to ge what I am expecting? Your help is much appreciated.


Solution

  • Well, both objects b1 and b2 have the same prototype, namely an instance of ObjA:

    ObjB.prototype = new ObjA();
    

    hence they inherit and have access its properties. b1.objLiteral and b2.objLiteral refer to the same object:

    enter image description here

    and

    > b1.objLiteral === b2.objLiteral
      true
    

    To fix that, you have to create a new object of each instance. This is normally done by calling the "parent" constructor inside the "child" constructor:

    function ObjB() {
        ObjA.call(this);
        this.propB = 2;
    }
    

    ObjA.call(this) will call ObjA and within that function, this will refer to the argument passed to call [MDN], in this case the new instance of ObjB:

    enter image description here

    As you can see, objLiteral is now property of each instance:

    > b1.objLiteral === b2.objLiteral
      false
    

    To avoid this confusion in the first place, setting an instance of the parent constructor as the prototype of the child constructor should be avoided. What if the parent constructor expects instance specific arguments? What would you pass to ObjA in this case?

    It's better to set the prototype of the child constructor to the prototype of the parent constructor (with one level of indirection) and call the parent constructor like above:

    function inherit(Child, Parent) {
        var Tmp = function() {};
        Tmp.prototype = Parent.prototype;
        Child.prototype = new Tmp();
        Child.prototype.constructor = Child;
    }
    
    inherit(ObjB, ObjA);
    

    Tmp to prevent extending Parent.prototype if you extend Child.prototype later on.


    Regarding propA:

    It "works", because you are assigning a new value to it.

    Here are again the objects, after assigning properties to x.objLiteral and x.propA. You can see that the objects don't have an own objLiteral property, but an own propA:

    enter image description here

    If instead you assign a new value to objLiteral, e.g. through

    b1.objLiteral = {hello: 42};
    

    b1 will now have its own property objLiteral, which shadows the inherited one:

    enter image description here