Inheritance breaks when setting prototype explicitly?

M

mmcloughlin

I'm learning about objects and am trying to figure out how basic
inheritance works. I've got into the habit of explicitly setting the
prototype object with an object literal as it seems to make the
creation of a class easier to read/understand.

Anyway it seems to break the inheritance chain in the following code
and I don't know why.


window.onload = function() {
var p = new Parent();
alert(p.property); // works

var c = new Child();
alert(c.property); // doesn't work, alerts "undefined"
}

function Child() {}
Child.prototype = new Parent();

function Parent() {}
Parent.prototype = {property: "Hello from Parent's prototype."};


If you replace the last line with

Parent.prototype.property = "Hello from Parent's prototype.";

then the whole thing works. Does anyone know why this is happening?
 
P

petermichaux

I'm learning about objects and am trying to figure out how basic
inheritance works. I've got into the habit of explicitly setting the
prototype object with an object literal as it seems to make the
creation of a class easier to read/understand.

Anyway it seems to break the inheritance chain in the following code
and I don't know why.


window.onload = function() {
var p = new Parent();
alert(p.property); // works

var c = new Child();
alert(c.property); // doesn't work, alerts "undefined"
}

function Child() {}
Child.prototype = new Parent();

function Parent() {}
Parent.prototype = {property: "Hello from Parent's prototype."};


If you replace the last line with

Parent.prototype.property = "Hello from Parent's prototype.";

then the whole thing works. Does anyone know why this is happening?

I struggled with chaining prototypes also. Then I found a great
tutorial

http://kevlindev.com/tutorials/javascript/inheritance/index.htm

Peter
 
R

Richard Cornford

Anyway it seems to break the inheritance chain in the
following code and I don't know why.

Your problem is the order in which your code executes. globally declared
functions, like your constructors, are processed during variable
instantiation, prior to the execution of any code for the global
execution context. Other code, such as assignments, is executed in
source code order.

When the functions are created their - prototype - properties are set to
the default value of an empty object instance.

function Child() {}
Child.prototype = new Parent();

So this assignment of a new instance of the object constructed with
the - Parent - constructor is happening after the creation of the -
Parent - and - Child - functions but before the assignment of a new
object to the - prototype - of Parent. The object construction process
assigns the value of the constructor function's - prototype - property
to the new object's internal - [[Prototype]] - property (which is the
property that is used in resolving property names), and it assigns the
value that the function's prototype has when the - new - expression is
evaluated.
function Parent() {}
Parent.prototype = {property: "Hello from Parent's prototype."};

Whatever you assign to the - Parent.prototype - here will be used for
the [[Prototype]] of all subsequent objects created with - new Parent -,
but not the ones already constructed.

The more normal formulation:-

Parent.prototype.property = "whatever";

- would not suffer this problem as it assigns a new property to the
object that is already the constructor's prototype, and so would have
been the object assigned to the internal [[Protoytpe]] property of
preceding - new Parent - calls.

Richard.
 
J

John G Harris

I'm learning about objects and am trying to figure out how basic
inheritance works. I've got into the habit of explicitly setting the
prototype object with an object literal as it seems to make the
creation of a class easier to read/understand.

Anyway it seems to break the inheritance chain in the following code
and I don't know why.


window.onload = function() {
var p = new Parent();
alert(p.property); // works

var c = new Child();
alert(c.property); // doesn't work, alerts "undefined"
}

function Child() {}
Child.prototype = new Parent();

This statement is executed before ...
function Parent() {}
Parent.prototype = {property: "Hello from Parent's prototype."};

... this statement.

Can you see why Child.prototype is given an object whose prototype isn't
the object you wanted it to be?

If you cut and paste the Parent work and put it before the Child work
the code does what it ought to do.

In general, it's nearly always a good idea to build parents before
children.

If you replace the last line with

Parent.prototype.property = "Hello from Parent's prototype.";

then the whole thing works. Does anyone know why this is happening?

Yes :)

John
 
M

mmcloughlin

Thank you for the replies. Richard and John, your replies are very
informative. Perhaps I could just run my understanding past you:

1.
Firstly, during variable instantiation the Child and Parent functions
are created, initially with empty Objects stored in their prototype
properties.

2.
Then the code is run in the order it appears. When we get to this line,
Child.prototype = new Parent();

Effectively what happens is this,

1 x = new Object();
2 Parent.apply(x);
3 x.__proto__ = Parent.prototype;
4 Child.prototype = x;

But at this stage Parent.prototype is an empty object, so
Child.prototype.__proto__ refers to an empty object.
The thing that I took a while to realise is that
Child.prototype.__proto__ does not maintain a reference to
Parent.prototype (which would be convenient), rather it refers to the
empty object.

3.
Hence when the line
Parent.prototype = {property: "Hello from Parent's prototype."};

is executed it changes the object Parent.prototype refers to, but not
the object Child.prototype.__proto__ refers to (this still refers to
the empty object). Thus we get the error.

4.
The reason
Parent.prototype.property = "Hello from Parent's prototype.";

works is because at this point both Parent.prototype and
Child.prototype.__proto__ refer to the same empty object, and this line
modifies the property property of that object.


Let me know if I'm wrong. Thanks for your help.

Mike
 
R

Richard Cornford

mmcloughlin said:
Thank you for the replies. Richard and John, your replies
are very informative. Perhaps I could just run my
understanding past you:

1.
Firstly, during variable instantiation the Child and
Parent functions are created, initially with empty
Objects stored in their prototype properties.

2.
Then the code is run in the order it appears. When we
get to this line,


Effectively what happens is this,

1 x = new Object();
2 Parent.apply(x);
3 x.__proto__ = Parent.prototype;
4 Child.prototype = x;

But at this stage Parent.prototype is an empty object, so
Child.prototype.__proto__ refers to an empty object.
The thing that I took a while to realise is that
Child.prototype.__proto__ does not maintain a reference to
Parent.prototype (which would be convenient), rather it
refers to the empty object.

3.
Hence when the line


is executed it changes the object Parent.prototype refers
to, but not the object Child.prototype.__proto__ refers
to (this still refers to the empty object). Thus we get the
error.

4.
The reason


works is because at this point both Parent.prototype and
Child.prototype.__proto__ refer to the same empty object,
and this line modifies the property property of that object.


Let me know if I'm wrong. Thanks for your help.

Yes, that is it exactly (except for maybe quibbling about the use of the
JavaScript(tm) extension __proto__ in place of the ECMA 262
[[Prototype]] terminology, but that in JavaScript(tm) the root of the
prototype chain is exposed as __proto__ doesn't change the mechanics or
the sequence).

Richard.
 
J

John G Harris

Thank you for the replies. Richard and John, your replies are very
informative. Perhaps I could just run my understanding past you:
<snip>

What you say is ok, but I'm going to add a couple of extra remarks.

But at this stage Parent.prototype is an empty object,
<snip>

If you draw a picture of the prototype chain it's worth remembering that
every chain ends with the special prototype object. This special object
holds the esoteric system methods such as hasOwnProperty and
isPrototypeOf.

Here the prototype chain will be a new (and empty) object followed by
the special object. If you had tried to assign null the system would
have ignored you and assigned the special object instead (that's in
recent, ECMAScript v3, browsers).

<snip>

An object pseudo-literal (aka object initialiser) can be neat, but you
have no way of setting the object's prototype. It will be the special
prototype object. This is all right for the first layer of inheritance
(Parent) as that's exactly what you want.

For the second layer (Child) and beyond it's obviously useless. Here
you've used a new Parent as Child's prototype. This has its problems.
First, you need to attach Child's methods afterwards : it's starting to
look scruffy. Second, a Parent object might have data properties such as
names. At best these properties are redundant; at worst they can produce
confusing symptoms when you are chasing an obscure bug. Third, the
Parent constructor can have parameters, and these might be checked for
legality. ("Only make these on a Tuesday." Whoops!)


My preference is to use a full-blown constructor for building prototype
objects. You can add just the properties needed, and you can attach
whatever prototype chain is needed (provided it's been built at this
point in the code, of course). The constructor is used only once, but
the code is laid out in a way that is familiar and it's easy to check.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,821
Latest member
AleidaSchi

Latest Threads

Top