1

I am new to Javascript and have a question about using inheritance with it

This article has been helpful so far (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#More_flexible_constructors), but the behaviour I am observing in Chrome's debugger does not seem to be matching what I'd expect to see.

I have two cases:

Base = function () {
    this.value = 0;
};

Base.prototype.constructor = Base;

Derived = function () {
    Base.call(this);
};

Derived.prototype = new Base();
Derived.prototype.constructor = Derived;

In the debugger I see this:

enter image description here

enter image description here

After stepping over the assignment, I see this

enter image description here

The value in the instance has changed but the value in the prototype has not. Is this to be expected?

The second approach I do not quite understand is this (referenced here again - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model#More_flexible_constructors (see the code snipped at the bottom of section 'Property Inheritance Revisited')

Base = function () {
};

Base.prototype.constructor = Base;
Base.prototype.value = 0;

// Derived as before...

enter image description here

and after the assignment

enter image description here

Value has been added as a new property. Shouldn't this have changed the value in the prototype? Or do I explicitly need to access the value by going through the prototype - e.g. derived.value.prototype.value = 5

Any information on this would be greatly appreciated!

Thanks!

UPDATE:

Thank you to everyone who responded, it turns out it was to do with my not using Object.create at the appropriate time. I updated my code to this:

enter image description here

and in the debugger I got what I expected:

enter image description here

This looks right! :)

enter image description here

Thank you to @sixfingeredman and @Bergi for your help!

Tom
  • 1,956
  • 2
  • 18
  • 23
  • Yes, this is expected. – Bergi Nov 16 '14 at 18:37
  • Better not create instance of Parent to set prototype of Child. Instead use Object.create inheritance and mix ins is explained in detail here: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Nov 16 '14 at 23:36

2 Answers2

2

First, while not on topic, remove this line:

Base.prototype.constructor = Base;

It isn't doing anything useful.

Now to your question, yes, the behavior is expected. Imagine if it updated the .prototype. Because the .prototype is shared between all instances, all instances would see the update. That for sure wouldn't be what you want.

When you did this:

var derived = new Derived();

you created a new object that inherits from Derived.prototype.

And when you did this:

derived.value = 1;

you assigned a value directly to that new object.

So before the assignment, the chain of inheritance looked like this:

  derived            Derived.prototype      Base.prototype       Object.prototype
+-----------+         +-----------+         +-----------+         +-----------+
|           |         |           |         |           |         |           |
|           |---------|  value:0  |---------|           |---------|           |
|           |         |           |         |           |         |           |
+-----------+         +-----------+         +-----------+         +-----------+

And after the assignment, it looks like this:

  derived            Derived.prototype      Base.prototype       Object.prototype
+-----------+         +-----------+         +-----------+         +-----------+
|           |         |           |         |           |         |           |
|  value:1  |---------|  value:0  |---------|           |---------|           |
|           |         |           |         |           |         |           |
+-----------+         +-----------+         +-----------+         +-----------+

So before the assignment, if you looked up value on your derived object, it wouldn't be found, so it would jump over to Derived.prototype, where it would find it.

But after the assignment, the value would be found directly on your derived object.


And with your second way, the prototype chain would start of looking like this instead:

  derived            Derived.prototype      Base.prototype       Object.prototype
+-----------+         +-----------+         +-----------+         +-----------+
|           |         |           |         |           |         |           |
|           |---------|           |---------|  value:0  |---------|           |
|           |         |           |         |           |         |           |
+-----------+         +-----------+         +-----------+         +-----------+

So when looking up value on derived, it would traverse from derived to Derived.prototype to Base.prototype before it finds the value property.


So let's say you create a bunch of objects from the Derived constructor. The sketches above would change so that each one of the objects you created would point to Derived.prototype.

  derived_1
+-----------+        
|           |
|           |
|           |
+-----------+
             \______
                    \
                     \
  derived_2          Derived.prototype      Base.prototype       Object.prototype
+-----------+         +-----------+         +-----------+         +-----------+
|           |         |           |         |           |         |           |
|           |---------|           |---------|  value:0  |---------|           |
|           |         |           |         |           |         |           |
+-----------+         +-----------+         +-----------+         +-----------+
                      /
               ______/
  derived_3   /
+-----------+        
|           |
|           |
|           |
+-----------+        

Now you can probably see why you don't want to update the prototype.

If we did derived_1.value = 42 and if it updated the Derived.prototype object, then when you went to look up value from derived_2 and derived_3, you'd get the value that you assigned to derived_1.

six fingered man
  • 2,460
  • 12
  • 15
  • Hey there! Thank you very much for an awesome response. I think I understand a little better now about what is actually happening under the hood, but I don't see how I would 'correctly' use inheritance. In both of these cases, while doing what is expected, they are not doing what I actually want. In the first case, instead of creating a new value property, how would I modify the one on the prototype? – Tom Nov 16 '14 at 19:19
  • @Tom: You'd modify the prototype in the same way that you currently are... by doing `Derived.prototype.foo = "bar";`. Just keep in mind that unless each object has its own `foo` property, they'll all see the same `"bar"` value. The only time you want to update a `.prototype` is to share data between all instances. The most common example is providing methods to all the objects. – six fingered man Nov 16 '14 at 19:36
  • Also if I understand correctly would you use the prototype to store a variable in a similar way to making a member static on a class in say C# or C++? – Tom Nov 16 '14 at 19:36
  • @Tom: If a static member is a value observed from all instances, then the answer is "sort of". The trouble is that for primitive data like numbers and strings, your instances would have to go to the trouble of specifically modifying the `.prototype` of its constructor. If one accidentally declares its own property, then it won't get the `.prototype` one anymore. For those sorts of static variables, it's probably more common to put it on the constructor function itself. `Derived.value = "42"`. Now the value must be accessed via the constructor function instead of `this`. – six fingered man Nov 16 '14 at 19:40
  • Cool thanks, I have updated my question and have got the behaviour I want, and understand JS a lot better now too! :) Thank you for your help! – Tom Nov 16 '14 at 20:00
2

Value has been added as a new property. Shouldn't this have changed the value in the prototype?

No, this behaviour is expected. Properties are inherited only on "getting", not on setting. Once you assign a value to a property of an object1, the property on that object is changed (or created).

1: except there is an accessor property (setter function) on the prototype

Or do I explicitly need to access the value by going through the prototype - e.g. derived.value.prototype.value = 5

To change the value on the prototype, you would need to assign it on the prototype, yes. In your case, that would be

Derived.prototype.value = 5;

or

Object.getPrototypeOf(derived).value = 5;

Btw, notice that the property (which is created per-instance in the Base constructor) should not exist at all on Derived.prototype - see What is the reason to use the 'new' keyword at Derived.prototype = new Base. Instead of Derived.prototype = new Base; you should be using

Derived.prototype = Object.create(Base.prototype);
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for a very helpful answer, I will repeat my test case using Object.create(..) to see if that produces the behaviour I want. – Tom Nov 16 '14 at 19:20
  • Your tip about using Object.create(..) was on the money, I've updated my question and my problem is solved, thanks! – Tom Nov 16 '14 at 19:59