OOP in JavaScript (part 2)
I already wrote an article on this topic (Object Oriented Programming in JavaScript (Extending / Inherit classes)), but last few days I realized that there are some things that I didn't explain.
First of all I added a new line to the function that makes the extending. The old version was:
function extend(ChildClass, ParentClass) {
ChildClass.prototype = new ParentClass();
ChildClass.prototype.constructor = ChildClass;
}
The new one is:
function extend(ChildClass, ParentClass) {
ChildClass.prototype = new ParentClass();
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype.superclass = ParentClass;
}
I.e. I'm adding a new property called superclass, which actually holds a reference to the ParentClass. The inheritance still works:
// extend function
function extend(ChildClass, ParentClass) {
ChildClass.prototype = new ParentClass();
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype.superclass = ParentClass;
}
// the parent class
var Parent = function () {
this.name = "Parent";
};
Parent.prototype = {
getName: function () {
return this.name;
}
};
// the child class
var Child = function () {
this.name = "Child";
};
Child.prototype = {};
extend(Child, Parent);
var c = new Child();
log(c.getName());
The log function simply adds string to a div. The result of the code above is Child and could be seen here. If we comment extend(Child, Parent); the getName will not exist in Child class and the browser will throw an error c.getName is not a function.The extend function also works if you have more than one level of inheritance. I.e. if you want to extend Child the getName method will be still available. Example of this approach could be seen here. During the usage of this tehnique I came across couple of problems and I decided to share the solutions.
Creating methods in the child classes
As you can see, we overrided the prototype of our Child class in order to make possible the extending. This means that if you have some methods defined you will lose them. For example code like this one will produce an error c.getFullName is not a function
// the parent class
var Parent = function () {
this.name = "Parent";
};
Parent.prototype = {
getName: function () {
return this.name;
}
};
// the child class
var Child = function () {
this.name = "Child";
};
Child.prototype = {
getFullName: function () {
return "The name is " + this.getName();
}
};
extend(Child, Parent);
var c = new Child();
log(c.getFullName());
To avoid this problem you have to define the methods of the class inside its constructor:
var Child = function() { this.name = "Child"; this.getFullName = function() { return "The name is " + this.getName(); };};
Check the result here.
Accessing method of the parent class
The superclass property was added because of that. Sometimes you will override the methods that are coming from the parent class, but there are also cases when you will need access to the previous version. For example:
// the parent class
var Parent = function () {
this.name = "Parent";
};
Parent.prototype = {
getName: function () {
return this.name;
}
};
// the child class
var Child = function () {
this.name = "Child";
this.getName = function () {
return "The name is " + this.superclass.prototype.getName.call(this);
};
};
extend(Child, Parent);
var c = new Child();
log(c.getName());
As you probably noticed I overrided the getName method of the Parent class. I.e. it doesn't return this.name. But because we have access to the prototype where the method is still available we can call it. It is important to call the method like I did it above, because in the other way this in return this.name; will be undefined. Check out the full example's code here.
Calling the parent constructor
To solve this problem I used superclass property again.
// the parent class
var Parent = function (name) {
this.name = name;
};
Parent.prototype = {
getName: function () {
return this.name;
}
};
// the child class var
Child = function () {
this.superclass("Child");
};
extend(Child, Parent);
var c = new Child();
log(c.getName());
The full code here.