Inheritance request for comment

R

Robert Spoons

Can you look over this code, preferably try it, and comment?

I believe the 'extend' function below will allow you to use full
'class inheritance' in javascript, but I would like to verify it.

The extend function allows the following:
1) inheriting from multiple classes,
2) inheriting an inherited classes inheritances (awkward to say),
3) inheriting appended prototype methods and properties of inhertited
classes.

Thanks.

//----------------------------------------------------------
//rspoons
//'extend' must have two arguments, the object to inherit and
//an Array of arguments to supply the superclass.
//If the superclass requires no arguments, [null] must be passed.
Object.prototype.extend=function(){
if(arguments[1]){ //make sure argument(s) are supplied
for(var i=0;i<arguments[1].length;i++){
if(typeof(arguments[1])=="string")
arguments[1]="'"+arguments[1]+"'"
}
if(arguments[1].length>0){Arguments=arguments[1].join(",")}
else{Arguments=arguments[1][0]}
for(var i in eval("new arguments[0]("+Arguments+")")){
if(i!='extend'){
g=eval("new "+arguments[0]+"("+arguments[1]+")."+i)
if(typeof(g)=="undefined"){
eval("this.prototype."+i+"=new
arguments[0]("+arguments[1]+")."+i)
}
else{
eval("this.prototype."+i+"=g")
}
}
}
}
}
//an oval class
function Oval(xaxis,yaxis){
this.xaxis=xaxis
this.yaxis=yaxis
this.classid="oval"
}

//a color class
function Color(color){
this.color=color
}
Color.prototype.RED="#ff0000"
Color.prototype.GREEN="#00ff00"
Color.prototype.BLUE="#0000ff"

//point class - a base class
function Point(x,y){
this.x=x
this.y=y
}

//SUBCLASSES INHERIT APPENDED CLASS METHODS AND PROPERTIES.
Point.prototype.moveto=function(x,y){this.x=x;this.y=y}
Point.prototype.moveby=function(dx,dy){this.x+=dx;this.y+=dy}

//circle class - a subclassing class
//OBJECTS CAN INHERIT FROM MULTIPLE CLASSES.
function Circle(radius,color){
Circle.extend(Oval,[radius,radius]) //inherits from the more
generic oval class
Circle.extend(Color,[color]) //also inherits from the color class
this.classid="circle"
}

//No arguments
function CompanyTag(){
this.company="someco"
this.author="Robret Spoons"
this.date="01.10.04"
}

//mycircleclass subclasses a suclassing class
//FULL OOP CLASS INHERITANCE
function MyCircleclass(radius,color,x,y){
MyCircleclass.extend(Circle,[radius,color]) //Inherits from Circle
MyCircleclass.extend(Point,[x,y]) //Inherits from Point
MyCircleclass.extend(CompanyTag,[null]) //Inherits from
CompanyTag - no arguments
this.classid="mycircleclass"
}

//Simple utility method to itterate through and
//return an objects enumerable methods and properties
function showprops(obj){
var s=""
for(var i in obj)
if(i!='extend')s+=i+": "+eval("obj."+i)+"\n"
return s
}

//CONSTRUCTING THE SUBCLASSED OBJECT AND USING AN INHERITED METHOD
mycircle=new MyCircleclass(13,"blue",100,120)
mycircle.moveby(2,-4)
mycircle.color=mycircle.RED
alert("mycircle\n"+showprops(mycircle)) //shows all enumerable methods
and properties

Thanks
 
L

Lasse Reichstein Nielsen

Can you look over this code, preferably try it, and comment?

First of all, your code is broken -- in particular some long comment
lines are broken onto the next line :) Please make sure your newsclient
doesn't break lines or post only lines so short that they are not broken.
Javascript code will not survive being wrapped as well as text, and
everybody who wants to try your code must first find and fix the errors
your newsclient has introduced (on a good day, they try, on a bad day,
they move on without commenting at all).
I believe the 'extend' function below will allow you to use full
'class inheritance' in javascript, but I would like to verify it.

Define "full class inheritance". First, define what "class" means in
a Javascript context, where there are no classes.
The extend function allows the following:
1) inheriting from multiple classes,
2) inheriting an inherited classes inheritances (awkward to say),
3) inheriting appended prototype methods and properties of inhertited
classes.

If I read it correctly, it copies the properties of the prototype
of the class and calls(applies) the constructor.

First, code comments:
//If the superclass requires no arguments, [null] must be passed.

Why not the empty array? (or nothing, you should be able to check that)
Object.prototype.extend=function(){

Why not
Function.prototype.extend=function(class,args) {
?
Instead of writing arguments[0] and arguments[1] everywhere?
if(arguments[1]){ //make sure argument(s) are supplied
for(var i=0;i<arguments[1].length;i++){
if(typeof(arguments[1])=="string")
arguments[1]="'"+arguments[1]+"'"

What if the arguement is the Javascript string "a hen's feather"? Or
"String literals use a \\ to write newlines\nYou just write \\n."?
You should also escape quotes, backslashes, special characters (ASCII<32), etc.
}
if(arguments[1].length>0){Arguments=arguments[1].join(",")}
else{Arguments=arguments[1][0]}

All this is only necessary if you use eval. Don't use eval. Never use
eval.
for(var i in eval("new arguments[0]("+Arguments+")")){

Use this instead (no eval):
function classCloner () { return class.apply(this,args); };
classCloner.prototype = class.prototype;
var newInstance = new classCloner();
for (var i in newInstance) {
if(i!='extend'){
g=eval("new "+arguments[0]+"("+arguments[1]+")."+i)

Fails if the object has property names like "foo bar" or "2".
Use:

g = newInstance;

Then you don't need to create a new object for each property.
if(typeof(g)=="undefined"){
eval("this.prototype."+i+"=new
arguments[0]("+arguments[1]+")."+i)

if g is undefined, you create a new object *the same way* and take
the same property again.

this.prototype=newInstance;
}
else{
eval("this.prototype."+i+"=g")

this.prototype = g;
}
}
}
}
}


Ok, my version:

function newApply(class,argsArray) {
function createApply() {
return class.apply(this,argsArray);
}
createApply.prototype = class.prototype;
return new createApply();
}

Function.prototype.extend = function extend(class,argsArray) {
var instance = newApply(class,argsArray);
for (var i in instance) {
if (i != "extend") {
this.prototype=instance;
}
}
}

Notice that you don't extend the object with a *class*, but with an
*instance* of the class. Already there, it breaks with how it works in
class based object oriented languages.

....
//circle class - a subclassing class
//OBJECTS CAN INHERIT FROM MULTIPLE CLASSES.
function Circle(radius,color){
Circle.extend(Oval,[radius,radius]) //inherits from the more
generic oval class
Circle.extend(Color,[color]) //also inherits from the color class
this.classid="circle"
}

You change the properties of the prototype inside the constructor.
That means that if you create two objects with this constructor,
the second one overwrites the properties of the first ones prototype.
Since they extend with different objects, the result is wrong.

Example

var c1 = new Circle(2,"#ff0000");
var c2 = new Circle(4,"#000000");
alert(c1.color); // gives #000000

Either add the new properties directly to the object being created:
I.e., here:
this.extend(Oval,[radius,radius]);
and in extend:
this = instance;
(that is, make objects "inherit" properties from other objects)
or only extend the class once:
Circle10.extend(Oval,[10,10]);

The problem is that you want to make classes inherit from instances.
That really doesn't make much sense, conceptually.

And:
if(i!='extend')s+=i+": "+eval("obj."+i)+"\n"
don't use eval:
if(i!='extend')s+=i+": "+obj;

<URL:http://jibbering.com/faq/#FAQ4_39>

/L
 
R

Robert Spoons

Lasse Reichstein Nielsen said:
Can you look over this code, preferably try it, and comment?

First of all, your code is broken -- in particular some long comment
lines are broken onto the next line :) Please make sure your newsclient
doesn't break lines or post only lines so short that they are not broken.
Javascript code will not survive being wrapped as well as text, and
everybody who wants to try your code must first find and fix the errors
your newsclient has introduced (on a good day, they try, on a bad day,
they move on without commenting at all).
I believe the 'extend' function below will allow you to use full
'class inheritance' in javascript, but I would like to verify it.

Define "full class inheritance". First, define what "class" means in
a Javascript context, where there are no classes.
The extend function allows the following:
1) inheriting from multiple classes,
2) inheriting an inherited classes inheritances (awkward to say),
3) inheriting appended prototype methods and properties of inhertited
classes.

If I read it correctly, it copies the properties of the prototype
of the class and calls(applies) the constructor.

First, code comments:
//If the superclass requires no arguments, [null] must be passed.

Why not the empty array? (or nothing, you should be able to check that)
Object.prototype.extend=function(){

Why not
Function.prototype.extend=function(class,args) {
?
Instead of writing arguments[0] and arguments[1] everywhere?
if(arguments[1]){ //make sure argument(s) are supplied
for(var i=0;i<arguments[1].length;i++){
if(typeof(arguments[1])=="string")
arguments[1]="'"+arguments[1]+"'"

What if the arguement is the Javascript string "a hen's feather"? Or
"String literals use a \\ to write newlines\nYou just write \\n."?
You should also escape quotes, backslashes, special characters (ASCII<32), etc.
}
if(arguments[1].length>0){Arguments=arguments[1].join(",")}
else{Arguments=arguments[1][0]}

All this is only necessary if you use eval. Don't use eval. Never use
eval.
for(var i in eval("new arguments[0]("+Arguments+")")){

Use this instead (no eval):
function classCloner () { return class.apply(this,args); };
classCloner.prototype = class.prototype;
var newInstance = new classCloner();
for (var i in newInstance) {
if(i!='extend'){
g=eval("new "+arguments[0]+"("+arguments[1]+")."+i)

Fails if the object has property names like "foo bar" or "2".
Use:

g = newInstance;

Then you don't need to create a new object for each property.
if(typeof(g)=="undefined"){
eval("this.prototype."+i+"=new
arguments[0]("+arguments[1]+")."+i)

if g is undefined, you create a new object *the same way* and take
the same property again.

this.prototype=newInstance;
}
else{
eval("this.prototype."+i+"=g")

this.prototype = g;
}
}
}
}
}


Ok, my version:

function newApply(class,argsArray) {
function createApply() {
return class.apply(this,argsArray);
}
createApply.prototype = class.prototype;
return new createApply();
}

Function.prototype.extend = function extend(class,argsArray) {
var instance = newApply(class,argsArray);
for (var i in instance) {
if (i != "extend") {
this.prototype=instance;
}
}
}

Notice that you don't extend the object with a *class*, but with an
*instance* of the class. Already there, it breaks with how it works in
class based object oriented languages.

...
//circle class - a subclassing class
//OBJECTS CAN INHERIT FROM MULTIPLE CLASSES.
function Circle(radius,color){
Circle.extend(Oval,[radius,radius]) //inherits from the more
generic oval class
Circle.extend(Color,[color]) //also inherits from the color class
this.classid="circle"
}

You change the properties of the prototype inside the constructor.
That means that if you create two objects with this constructor,
the second one overwrites the properties of the first ones prototype.
Since they extend with different objects, the result is wrong.

Example

var c1 = new Circle(2,"#ff0000");
var c2 = new Circle(4,"#000000");
alert(c1.color); // gives #000000

Either add the new properties directly to the object being created:
I.e., here:
this.extend(Oval,[radius,radius]);
and in extend:
this = instance;
(that is, make objects "inherit" properties from other objects)
or only extend the class once:
Circle10.extend(Oval,[10,10]);

The problem is that you want to make classes inherit from instances.
That really doesn't make much sense, conceptually.

And:
if(i!='extend')s+=i+": "+eval("obj."+i)+"\n"
don't use eval:
if(i!='extend')s+=i+": "+obj;

<URL:http://jibbering.com/faq/#FAQ4_39>

/L


/*
Lasse Reichstein Nielsen, Thanks for your commentary - it did help.
I will be more specific in the wording I choose.
I still use eval in the function.

Thanks, I've updated the extend function - it addresses all relavent
points you made concerning the origional, and it now acts the way I
desire the function to act.
Also, the new version is much cleaner.
The function will add all publically enumarable properties and methods
of the specified target object to the calling object (effective
inheritance). I have attempted to include example use.
*/

/*
--------------------------
Much cleaner Inheritence function
Use :must be inside of object's constructor
this.extend(new ObjA(args))
(The object Inherits all enumerable properties and methods of ObjA)
*/
// obj is the object instance to inherit from
// i is an Enumerable Property or Method
// insta is a Property or Method Instance
Object.prototype.extend=function(obj){
for(var i in obj){
insta=eval("obj."+i)
eval("this."+i+"=insta")
}
}
/*
--------------------------
*/
function point(x,y){
this.x=x
this.y=y
}
function location(x,y){
this.extend(new point(x,y))
}
location.prototype.moveTo=function(x,y){this.x=x;this.y=y}

function oval(xrad,yrad){
this.radiusX=xrad
this.radiusY=yrad
}
oval.prototype.meth=function(){alert("ok")}

function circle(rad,color){
this.extend(new oval(rad,rad))
this.color=color
}
circle.prototype.area=function(){return
2*Math.PI*Math.pow(this.radiusX,2)}

function myCircle(rad,color,x,y){
this.extend(new circle(rad,color))
this.extend(new location(x,y))
}

Object.prototype.showProps=function(){
var s=""
for(var i in this)
if(!(i=='extend'||i=='showProps'))s+=i +": "+eval("this."+i)+"\n"
return s
}
a=new myCircle(3,"the hen's blue feather \n",5,10)
b=new myCircle(10,"red",100,120)
c=new oval(4,5)
alert("a\n"+a.showProps()+"\nb\n"+b.showProps()+"\nc\n"+c.showProps())
alert("a area = "+a.area() +"\nb area = "+b.area())
 
L

Lasse Reichstein Nielsen

(e-mail address removed) (Robert Spoons) writes:

Please trim the quotes :)
Use :must be inside of object's constructor
this.extend(new ObjA(args))

Why? You can always call
someObject.extend(someOtherObject)
// insta is a Property or Method Instance

You don't declare insta as a local variable.

I still use eval in the function.
insta=eval("obj."+i)
eval("this."+i+"=insta")

Why not just
this = obj;
It is equivalent when the property name is a valid identifier,
and, unlike your version, it works when it is not.

/L
 
D

Douglas Crockford

What advantage is there to using

Object.prototype.extend=function(obj){
for(var i in obj){
insta=eval("obj."+i)
eval("this."+i+"=insta")
}
}

instead of

Object.prototype.extend = function(obj){
for (var i in obj){
this = obj;
}
};
 

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,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top