Everyone knows Javascript doesn�t have classes. Thankfully most Javascript programmers are very good at playing pretend.
Because so many people attempt to introduce a classical structure to
their Javscript code, I figure I�ll share some of my findings. Below is
one way of allowing a base class function to copy an instance of a
subclass while maintaining proper type.
Let�s say we have an Animal class and we subclass it to create a Cat
class. We also want the Animal class to have a clone() method that will
return work with its subclasses. That is, if I clone a Cat, I want
�instanceof Cat� and �instanceof Animal� to both be true.
Let�s make the classes real quick. Here�s an Animal:
function Animal() {
// animal stuff
}
// All of Animal's methods will go into its prototype
Animal.prototype = {
saySomething: function() {
return "I am an animal!";
},
copy: function() {
var newcopy;
if (this.subConstructor) {
newcopy = new this.subConstructor;
} else {
newcopy = new Animal();
}
// maybe copy over other values
return newcopy;
}
};
And then the Cat. Typically the easiest way to make a Javascript
function be a �subclass� of another function is to set the prototype of
the subclass equal to �new baseclass.� With cat we�ll go a little
further and also save the constructor.
Cat:
function Cat() {
this.constructor(); // call animal's constructor
// cat stuff
}
// Save the cat's constructor, replace Cat's prototype and constructor
// Then restore cat's constructor as subConstructor
var con = Cat.prototype.constructor;
Cat.prototype = new Animal();
Cat.prototype.constructor = Animal;
Cat.prototype.subConstructor = con
// override the saySomething function
Cat.prototype.saySomething = function() {
return "Meow!";
};
There you have it. Cats created this way will be �instanceof Cat� and
�instanceof Animal�, and so will their clones. We should of course make
tests to see, and lucky for us all of them check out OK:
// some tests and their results in the comment
var c = new Cat();
console.log(c instanceof Animal); // true
console.log(c instanceof Cat); // true
console.log(c.saySomething()); // "Meow!"
var a = new Animal();
console.log(a instanceof Animal); // true
console.log(a instanceof Cat); // false
console.log(a.saySomething()); // "I am an animal!"
var copyCat = c.copy();
console.log(copyCat instanceof Animal); // true
console.log(copyCat instanceof Cat); // true
console.log(copyCat.saySomething()); // "Meow!"
var copyAnimal = a.copy();
console.log(copyAnimal instanceof Animal); // true
console.log(copyAnimal instanceof Cat); // false
console.log(copyAnimal.saySomething()); // "I am an animal!"
You might want to use the four lines needed for subclassing elsewhere
so its a good idea to stuff them into a function that you�d put in a
utility class/var somewhere. So we could make:
function derive(base, sub) {
var con = sub.prototype.constructor;
sub.prototype = new base();
sub.prototype.constructor = base;
sub.prototype.subConstructor = con
}
And then making a clone-able subclass would be as easy as calling derive after each constructor:
function Cat() {
this.constructor(); // call animal's constructor
// cat stuff
}
derive(Animal, Cat);
// override the saySomething function
Cat.prototype.saySomething = function() {
return "Meow!";
};
|