Prototyping - explanation required

W

Will

function obj1() {
this.children;

this.Children = function() {
if(typeof(this.children) === 'undefined') {
this.children = new col();
}
return this.children;
}

this.Children().Add(1);
}

obj2.prototype = new obj1();
function obj2() {
obj1.call(this);
this.Children().Add(1);
}

obj3.prototype = new obj2();
function obj3() {
obj2.call(this);
this.Children().Add(1);
}

function col() {
var arr = new Array();

this.Add = function(item) {
arr[arr.length] = item;
}

this.Count = function(item) {
return arr.length;
}
}

var a = new obj3();
alert(a.Children().Count());

I have been learning how to use prototyping with private static
members and today noticed something new to me. In the above example, 6
is returned when 3 would be desirable.

Turning the public member "this.children" into a private member (e.g.
"var children") corrects the issue, as does removing the lines
"obj1.call(this)" and "obj2.call(this)".

Am I correct in deducing that any public members in a prototyped
object will be re-evaluated for each derived object? So I'm guessing
that in this case, when obj3 is instantiated:

1) obj1, obj2 and obj3 all call children.Add() in the constructor
(Count = 3)
2) obj1.call(this) causes obj1 to call children.Add() again
3) obj2.call(this) causes obj2 to call children.Add() again, and also
causes a repeat of (2).

As a result children.Add() is called 6 times instead of 3.

Can anyone explain the above behaviour or correct my interpretation? I
am curious to know what xactly is happening and how to work around it.

Thanks

Will
 
L

Lasse Reichstein Nielsen

function obj1() {
this.children;

This line does nothing (except perhaps evaluating to undefined).
I assume it was originally a "var children" line :)
this.Children = function() {
if(typeof(this.children) === 'undefined') {

(you only need == for string comparison. If you wrote
if(this.children === undefined)
i.e., compare to the value itself, not a string, then the === is
needed. Still, better safe than sorry :)
this.children = new col();
}
return this.children;
}

this.Children().Add(1);
}
obj2.prototype = new obj1();
function obj2() {

I find it slightly confuzing to set a property of something that is
not declared until the next line. I know ECMAScript declares all functions
before executing any code, but it is still ... unsetteling.
obj1.call(this);
this.Children().Add(1);
}


obj3.prototype = new obj2();
function obj3() {
obj2.call(this);
this.Children().Add(1);
}

function col() {
var arr = new Array();

this.Add = function(item) {
arr[arr.length] = item;
}

this.Count = function(item) {
return arr.length;
}
}

var a = new obj3();
alert(a.Children().Count());

I have been learning how to use prototyping with private static
members and today noticed something new to me. In the above example, 6
is returned when 3 would be desirable.
Turning the public member "this.children" into a private member (e.g.
"var children") corrects the issue, as does removing the lines
"obj1.call(this)" and "obj2.call(this)".

Yes. If you change children to a local variable of the obj1 function,
then the two later calls of obj1 will create Children functions that
operate on the new variable. So each new obj2 object will get its own
Children function and children variable.

If this.children is a property, the new Children function assigned
to obj2 objects will all refer to the same property of the obj1 object
that is obj2's prototype. Only one of them will find it undefined.

Removing the calls to obj1 and obj2 should lower the count, since they
perform three of the Add(1)'s.
Am I correct in deducing that any public members in a prototyped
object will be re-evaluated for each derived object?

I am not sure exactly what you mean, but I think the answer is no.
Members (i.e., properties of objects) are not evaluated at all, they
just sit there until you change them.
So I'm guessing that in this case, when obj3 is instantiated:

1) obj1, obj2 and obj3 all call children.Add() in the constructor
(Count = 3)
2) obj1.call(this) causes obj1 to call children.Add() again
3) obj2.call(this) causes obj2 to call children.Add() again, and also
causes a repeat of (2).

Let me try to figure out what happens. Then we can compare :)
(And after doing that, yes, I agree. Derivation follows:)

Some functions are defined. Then the following lines are executed:

obj2.prototype = new obj1();
This instantiates obj1instance.children and adds 1 to it.
State after this:
[obj2]--prototype-->[obj1ins:col(Count=1)]
(i.e., the obj2 function has a property called "prototype" that points
to an instans of obj1 with a collection with Count()==1)

obj3.prototype = new obj2();
This creates obj3 and an obj2 instance:
[obj3]--prototype-->[obj2ins]--<<proto>>-->[obj1ins:col(Count=1)]
This also executes the obj2 function:
obj1.call(this);
It creates a new this.Children function, but not a new children collection.
It then adds one to the collection.
this.Children().Add(1);
It adds another one.
State:
[obj3]--prototype-->[obj2ins]--<<proto>>-->[obj1ins:col(Count=3)]
Then we execute:
var a = new obj3();
This creates a new obj3 instance:
a = [obj3ins]--<<proto>>-->[obj2ins]--<<proto>>-->[obj1ins:col(Count=3)]
and executes the obj3 function:
obj2.call(this);
this.Children().Add(1);
The first call executes obj2:
obj1.call(this);
this.Children().Add(1);
The first call again executes obj1, which creates a new Children function
and does:
this.Children().Add(1);
That is, a total of three calls to Add(1), bringin us to a count of 6.


In short, if we show the unfolded function calls indented, the
code that is executed is:

obj2.prototype = new obj1();
this.children = new col();
this.Children().Add(1);
obj3.prototype = new obj2();
obj1.call(this);
this.Children().Add(1);
this.Children().Add(1);
a = new obj3();
obj2.call(this);
obj1.call(this);
this.Children().Add(1);
this.Children().Add(1);
this.Children.Add(1);
As a result children.Add() is called 6 times instead of 3.

I can't see why it should only be 3. You explicitly call functions
that Add(1). Nothing automatic is happening.
Can anyone explain the above behaviour or correct my interpretation? I
am curious to know what xactly is happening and how to work around it.

I hope this is readable. :)

/L
 

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,822
Latest member
israfaceZa

Latest Threads

Top