4

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.

supercoco
  • 512
  • 2
  • 7
  • 25

1 Answers1

3

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

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks Felix for your prompt answer. I understand calling the parent constructor (worked great). Still a little confused as to why propA "works" without calling the parent constructor. I understand from your answer that "both objects b1 and b2 have the same prototype, namely an instance of ObjA"). According to this, they should have the same value for propA as they are pointing to the same instance. Or... am I just extending b1 and b2 with a new propA and ignoring the parent propA? – supercoco Mar 29 '12 at 12:04
  • Another question on your inherit function. I don't understand the need for the level of indirection. What is the use of `Tmp`? – supercoco Mar 29 '12 at 12:08
  • `propA` "works", because you are assigning a new value to it. If you'd assign a new value to `objLiteral` it would "work" as well, e.g. `b1.objLiteral = {hello: 6}`. Instead, you are assigning a value to a property of the object assigned to `objLiteral`. `Tmp` is needed to prevent extending `Parent.prototype` (which you don't want) if you extend `Child.prototype` later on. Otherwise, all instances of all child "classes" would have the same properties (since they all inherit from `Parent.prototype`). – Felix Kling Mar 29 '12 at 14:32
  • Thanks for your time in giving these explanations. – supercoco Mar 29 '12 at 17:06