Email conversation
From | Julian Kennedy |
To | Me |
Subject | Adding methods to prototype |
Date | 18 February 2005 12:19 |
Sir,
I am a big fan of your site. Possibly the best of its kind on The Web.
I was just passing through, and noticed:
http://www.howtocreate.co.uk/tutorials/javascript/objects
The common method, *getTheArea, would be better assigned to the
constructor's prototype
(equally too the common initial zero values in the previous example ).
I realise, that you may have conciously made this decision, however, so as
not to confuse the issue further by bringing inheritance into the equation.
On a related note:
The reason for my visit was to find the answer to a forum question:
[forum address]
The problem is that Firefox is reporting *true for *[event].ctrlKey whether
or not CTRL is depressed
when the event is a SELECT *onchange event.
(possibly a minor issue)
Yours
Julian Kennedy
From | Me |
To | Julian Kennedy |
Subject | Re: Adding methods to prototype |
Date | 18 February 2005 14:27 |
Julian,
> I am a big fan of your site. Possibly the best of its kind on The Web.
Thankyou :)
> The common method, *getTheArea, would be better assigned to the
> constructor's prototype
Since I am creating the constructor, this is not really necessary. When I
create the constructor, I am also creating the prototype itself. So adding
them at this stage is the same as adding them using the prototype later.
Indeed, it would be possible to add it to the prototype, but the real
purpose of the prototype is to add the property to all instances of that
object, even if they are already existing objects. So for example I could
create several strings, then later decide to add a new method to them, so I
add it to the String.prototype, and they should all inherit that property.
> I realise, that you may have conciously made this decision, however, so as
> not to confuse the issue further by bringing inheritance into the equation.
And this is quite true :)
I am trying to give a general introduction, and while I know that you could
go absolutely mad:
http://www.kevlindev.com/tutorials/javascript/inheritance/
and use the prototype to try to make JavaScript behave more like C++ or
Java (perfectly legitimate use of it), this is unnecessary for most
JavaScript programming, and not something I would want to teach people in
an introductory JavaScript tutorial.
The only reason that page uses that construct to add methods is for
demonstration purposes later on. It is not necessary, and it makes code a
lot more easy to read (in my opinion) to put things inside the main
constructor as I have done. Otherwise you would have to jump from function
to function just to work out what properties or methods your object may
have.
I think maybe I will add something in about inheriting from a parent class
later, but I will still try to keep things simple.
> The problem is that Firefox is reporting *true for *[event].ctrlKey whether
> or not CTRL is depressed when the event is a SELECT *onchange event.
Hmm - has this been reported to bugzilla?
http://bugzilla.mozilla.org/
Mark 'Tarquin' Wilton-Jones - author of http://www.howtocreate.co.uk/
From | Julian Kennedy |
To | Me |
Subject | Re: Adding methods to prototype |
Date | 18 February 2005 16:01 |
Mark,
Thanks for the swift reply.
I won't try and drag you to far into this, as we both know what we're
talking about....
> Since I am creating the constructor, this is not really necessary. When I
> create the constructor, I am also creating the prototype itself. So adding
> them at this stage is the same as adding them using the prototype later.
Not sure what you mean there
// (trimmed down)
function mycircle(x,y,r) {
this.xcoord = x;
this.ycoord = y;
this.retArea = getTheArea; // #1
this.retCirc = function () { return ( Math.PI * this.radius * 2 ); }; // #2
}
I'm saying , of course, that #1 and #2 would be 'better' as:
mycircle.prototype.retArea = getTheArea;
mycircle.prototype.retCirc = function () { return ( Math.PI * this.radius * 2 ); };
..since the assignments then do not have to be made individually to each and
every new object.
It's probably a good idea to present methods in this way, but I'd encourage
you to follow on to show how these common members can then be attached to
the prototype instead. It's not quite as tricky as emulating subclassing,
but a step in that direction.
If I was reaching into the air for a criticism, I would pluck #2.
Here, there is danger - the risk of unwanted closures (variables acting
strangely; memory leaks in IE). In fact, the form in your example may indeed
be a closure - although 'this' is no normal variable.
If I had the fullest grasp of terminlogy, and the weight of secure knowledge
behind me, I would suggest NOT using that construction. However, I don't
have the required weight !
I can say that, being one of those 'self-appointed' solution providers at a
couple of JS forums, I have noticed that people will develop examples like
this until they unwittingly stray into closure territory, often leading to
things going wrong.
I'll think about that bug report. I've never become familiar with the
workings of the Mozilla foundation, so I could just end up lowering the
signal to noise ratio.
Will continue to recommend your site
Jude
From | Me |
To | Julian Kennedy |
Subject | Re: Adding methods to prototype |
Date | 19 February 2005 14:44 |
Julian,
I have made some updates, including an advanced section showing how to use
prototype, public and private properties and class inheritance:
http://www.howtocreate.co.uk/tutorials/javascript/objects#advobj
> mycircle.prototype.retArea = getTheArea;
> mycircle.prototype.retCirc = function () { ... };
> ..since the assignments then do not have to be made individually to each
> and every new object.
Well, there are advantages and disadvantages. It may be a little faster
since you do not need to recreate the methods each time. However, even so,
when you create a new instance of that object, internally, a new copy of
the method will still be created and attached to the object, so the time
savings would be minimal.
Aditionally, you deny yourself the possibility of creating private
properties, as a privileged method can only be declared within the bounds
of the constructor function.
(Personally, I do not like the way I defined the retArea method - as a
referenced function - but that was just to demonstrate that "if you see
someone's code with this in it, this is what they are doing")
As a more important point, using prototype for everything can be much less
readable, as it breaks your constructor object up into several pieces, so
trying to work out which methods and properties apply to what objects gets
much harder. Additionally, many scripters are not aware that you can use
the prototype, so passing your code onto someone else might cause them
problems maintaining it.
> It's probably a good idea to present methods in this way, but I'd encourage
> you to follow on to show how these common members can then be attached to
> the prototype instead.
Done :)
Thanks for the criticisms, hope something useful has come out of it.
Tarquin
From | Julian Kennedy |
To | Me |
Subject | Re: Adding methods to prototype |
Date | 20 February 2005 03:13 |
Attachments | scripts showing how the prototype chain works |
Mark,
I'm pretty much writing as I read, so excuse the piecemeal style.
First, just a plain error report:
testcircle.texture = 'smooth';
...
mycircle.prototype.texture = 'smooth';
...
alert(testcircle.texture); //alerts 'red' <-- !!
****** Public and private properties ******
In my view, 'private members' very much deserve those quotes.
They are very much an emulation, and certainly not 'real' properties
of the instance (rather the call object of their creation).
The rest is all good advice, of course.
Really, anyone reading who doesn't know the object model inside out
should leave all this well alone !
*** Sub-classes and class inheritance
[quote]
In case you are wondering, the way it works is to actually create a
mycircle, then assign that to the mysphere constructor prototype. Since the
prototype is a special object, what it does is instead of becoming a
mycircle, it copies the properties and values of the mycircle that it
created, and adds them to the prototype of the mysphere. That way, every
time a mysphere is created, it gets those properties and methods added to
it. Be warned that this means that just this once, the mycircle constructor
is actually run, but although the object properties are kept, the object it
creates is basically discarded
[/quote]
As per my previous. I don't agree with this version of events at all.
JK's view >>
A a mycircle instance is created and the default mycircle prototype
is replace with this new object. That is it.
[let's call the mycircle instance, circle1, just for reference]
Instances of mysphere [em]created while circle1 is the mysphere
prototype[/em]
will inherit the members that circle one has inherited from the object that
is it's prototype.
circle1's own properties are (in this case) all undefined, as it was
instantiated with no arguments in the constructor. No matter really.
<<
'what it does is instead of becoming a mycircle.....'
'but although the object properties are kept,
the object it creates is basically discarded'
No. It remains a mycircle. It just happens to be part of a prototype chain
now too.
Run [script1.js] in the attached zip.
(It's adapted for CScript, if you find dismissing alerts tiresome)
All that's going on is some musical chairs with objects.
Instances of a type will inherit from the object that happens
to be assigned to the prototype property of the constructor
when they are created. No more voodoo goes on than that.
The object assigned to the prototype remains what it is,
apart from the fact that we have hidden some of it's
inherited members in favour of own members for the purposes
of using it as the prototype for new type.
Remove these amendments and you get yer object back right as rain.
[quote]
Lines like:
window.anObject = this;
will cause problems.
[/quote]
I haven't worked out what this is getting at..yet...
[quote]
Unfortunately, I cannot simply call the constructor, because that would
just create a new mycircle.
[/quote]
..which is just what we want, but I certainly see what you mean.
Unless we want to give Sphere a prototype that provides default properties
we'll be instantiating a new Circle with no arguments. If Circle simply
assigns straight properties from passed arguments, I see no problem,
but, if Circle contains something more complicated that will result
in error (or other nastiness) if properties are undefined, then
we certainly don't want the whole constructor executed.
I don't think that a getReady method is actually necessary here.
Here's my usual solution to the problem:
function Thing(p0,p1,p2)
{
if(!arguments.length) return this;
this.p0 = p0
...etc..
}
Another approach would be to have a hierarchy of 'abstract' types,
('abstract' meaning meaning here 'only instantiated to provide prototypes')
Then only 'leaf' types would ever be instantiated to create objects
for normal use.
This would restrict us a fair bit though.
Your technique of putting the initiation of own members
into a separate 'initiator' (getReady) method is almost counterintuitive.
BUT in some ways it makes sense.
In our Java-like paradigm, it puts the constructor
into the position of the actual class, while the 'initiator'
does the job that a constructor would do in Java.
This would bring benefits in the same way. Namely that own
properties that both types always have can then be assigned
in one statement in both constructors, since the initiator is inherited.
My solution to this kind of thing is usually to assign the
super class constructor as the 'inherit' method:
function SuperThing(colour,number)
{
this.color = colour;
this.number = number;
}
SuperThing.prototype.doSomething = function(){ blah(this) }
function Thing(colour,number,smell)
{
this.inherit(colour,number);
this.smell = smell;
}
Thing.prototype = new SuperThing;
Thing.prototype.inherit = SuperThing;
Try [script2.js]
It's the circle/sphere example on your page translated into
'my way'.
Of course, one reason there's less code is that I have got into
the habit of using Type_ as a shortcut for Type.prototype,
but that's not quite all, I reckon.
For one thing, I'd argue that there's a little less juggling
to get Sphere into line, and that the whole structure was
possible before the appearance of the 'call' method
(not present in IE5)
Any good ?
Jude
From | Me |
To | Julian Kennedy |
Subject | Re: Adding methods to prototype |
Date | 22 February 2005 18:46 |
> alert(testcircle.texture); //alerts 'red' <-- !!
doh! fixed :)
> In my view, 'private members' [...] are very much an emulation
Absolutely. They are a by-product of closures, and personally I see little
use for them.
> Really, anyone reading who doesn't know the object model inside out
> should leave all this well alone !
Yep. basically I just want to let the Java programmers (who complain at me
that JavaScript can't do class inheritance) know that it is better than
they think.
> > it copies the properties and values of the mycircle
> No. It remains a mycircle. It just happens to be part of a prototype chain
> now too.
It appears I had misunderstood something about prototype chaining. After
creating a mysphere, I then add a property to the prototype of the mycircle,
and amazingly, the mysphere also got that property. JavaScript is smarter
than I realized. I was under the (mistaken) impression that it had
completely lost the reference to the mycircle prototype. Apparently not.
I will update that description, once I can put my thoughts into human words
;)
> Run [script1.js] in the attached zip.
interesting, I didn't realise you could also delete properties and get back
the inherited ones, but that refers to my statement above :)
> window.anObject = this;
var allObs = [];
function myobject() {
allObs[allObs.length] = this;
...etc...
}
I have seen stuff like this many times, where 'allObs' is then used to
refer to all objects of that class. However, if we did some inheritance
here, nasty things will happen, because allObs[index] will now refer to the
prototype of the inheriting class, and not the individual object.
> if(!arguments.length) return this;
why return 'this'? if we are creating a 'new whatever()' we have no need to
return anything. return; on its own should work.
> this.p0 = p0
Took a little while to get my head around this ...
Seems simple, but I was trying to work out exactly what 'this' referred to.
Normally, in a function, it refers to the window object.
You were refering to it as a method of the prototype, which should mean
that the properties are created as properties of the prototype, and not the
object itself. Of course, I had not realised that since we have now created
an instance of the mysphere, the method is now a method of the instance and
not the prototype. Bah! my poor head.
> there's a little less juggling
indeed, a plus point. thanks for the education :)
> (not present in IE5)
argh :( never tried in IE 5. I see it is an ECMAScript 3 method. Makes
sense.
> Any good ?
Very. :)
From | Me |
To | Julian Kennedy |
Subject | Re: Adding methods to prototype |
Date | 22 February 2005 20:46 |
Jude,
Ok, I retract part of my last email.
here is the problem with your technique. You cannot re-use the .inherit
method name. each time it replaces the previous ones, so if you try
something like this:
function Sphere2(x,y,z,r){
if(!arguments.length )return this;
this.inherit(x,y,z,r);
this.foo = 'bar';
}
Sphere2_ = Sphere2.prototype = new Sphere;
Sphere2_.inherit = Sphere;
var testsphere2 = new Sphere2(3,4,5,6);
You will get a recursive call to '.inherit', resulting in a script error
(or an endless loop). so each subclass would need to use a different name
for that method.
Since scripts that use classes this heavily may well have several levels of
inheritance, you would have to remember each .inherit method name.
The prototype.getready avoids this, because it attaches to the prototype
(and is accessed through the prototype, not directly). Unfortunately, since
it is not supported by IE 5 I will have to work out an alternative.
Maybe the only option is to use a simple system to remember the method name,
like maybe .inheritClassname, eg .inheritCircle or .inheritSphere
Am I correct in this analysis? Or do you already know of an alternative?
Cheers
Tarquin
From | Me |
To | Julian Kennedy |
Subject | Re: Adding methods to prototype |
Date | 22 February 2005 23:46 |
Jude,
apologies for the multiple emails ... :)
I have decided that the getready way is better, simply because it allows
the same name to be used each time without any conflicts, and without
needing to know how many parent classes there are above the current class.
It may be a longer way round than yours, but in the long run, I believe it
is for the better.
As for IE 5, that bit is simple. Instead of referencing the method then
using 'call', I use
this.tempReady = parent_class_name.prototype.getready;
this.tempReady(a,b,d);
tempReady will be overwritten at each stage as it passes back up the chain,
each time referencing the correct method, and will then run as a method of
the current object, ensuring that all new properties are attached to the
correct object.
Works like a charm. It's even working in Ye Olde IE 4 and NS 4 now, as well
as all of the current browsers. So I am happy with it in its current state.
I have also added in the other amendments (prototype chain, etc.)
Thanks for your input on this, it's been very useful.
Tarquin