OOP with JavaScript
First of all it should be said that OOP with JavaScript is a real pain. I have read documents on the net where JavaScript is held high for its simple OOP design but that's nonsense. Yes, JavaScript knows objects. And yes, it is possible to create custom objects in a very simple way. But one of the main reasons for OOP is inheritance and this is something which is possible but absolutely dirty in JavaScript. But ok, here you are, so you may be willing to learn it. Or maybe you know it better and can teach me a more clean approach. Feel free to contact me.
Simple classes
Using simple classes without inheritance is very simple. A class is just
a function. This function is the constructor for the class. This constructor
is called every time you create an instance of this class. Inside the
constructor you can use the this qualifier to access object
properties. You don't need to declare properties (And you can't do it either).
Just simply access them. Here is a simple example:
// Declare the Person class
function Person(firstName, lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
// Create an instance of the Person class
var person = new Person("Arthur", "Dent");
// Output the name of the person
document.writeln(person.firstName + " " + person.lastName);
Impressed? No? I can understand this. Chances are high that the above is not new to you. What you seek is more OOP. So what about class methods? Well, here they come.
Class methods
JavaScript has no support for real classes. Instead it uses something called prototypes. A prototype is an object which can be used as a template for creating new objects. So what you want to do if you want to use class methods is extending the prototype of the class you already have defined by writing the constructor-like function like the Person function in the last example. So lets implement our first method in the Person class. This method should return a string representation of the object or in other words the concatenated first name and last name of the person. Here we go:
Person.prototype.toString = function()
{
return this.firstName + " " + this.lastName;
};
Impressed? No? I know, it's ugly. But as far as I know it's the best way to do it. So what's happening if you create an instance of the Person class now? JavaScript creates a new object and uses the prototype of Person as a base object. And this prototype now knows an additional method: The toString() method.
By the way: If you want to use method arguments just define them inside the brackets behind the function keyword in the above example. They work like normal function arguments.
So now after we have a toString() method we can rewrite our output like this:
// Create an instance of the Person class
var person = new Person("Arthur", "Dent");
// Output the name of the person
document.writeln(person.toString());
Fine. Now we have a class, a constructor, properties and a method. Is this OOP enough for you? Do you miss something? Ah, inheritance. Be prepared for some real nasty stuff...
Inheritance
Simple but broken inheritance
As I already told you, JavaScript don't know real classes. It only knows prototypes. So we have to do inheritance with that prototype stuff. The basic is to use an instance of the class you want to extend as the base prototype and then extend this prototype.
Some docs on the net suggests an approach like this:
function BaseClass()
{
// Constructor for base class
}
function NewClass()
{
// Constructor for new class
}
// Derive NewClass from BaseClass
NewClass.prototype = new BaseClass();
// Create instance of NewClass
var obj = new NewClass();
This approach has some drawbacks. As you can see the constructor of the base class is called when creating the extended class. That's not what we want! Especially this does not work with our Person class because we have constructor arguments there and the above approach gives us no possibility to pass them to the constructor. Sure, we can pass arguments to the constructor in the line which derives NewClass from BaseClass but there we don't know what values should be passed to the constructor. So this whole approach is not useable and I will describe a somewhat more complicated but working approach in the next section.
In the following examples we will declare a second class called NickPerson. This class should extend the Person class and add another property to it: A nickname.
More complex but working inheritance
In the last section I told you that the classical inheritance has the drawback that constructors are not working as expected. A workaround for this problem is creating a temporary class with the same prototype as the super class we want to extend. From this temporary class we derive our sub class. This task needs some code lines so it's a good idea to implement them as methods inside JavaScript's internal Object class, so they are easily usable everywhere.
Object.prototype.inherit = function(superClass)
{
var tmpClass = function() {};
tmpClass.prototype = superClass.prototype;
this.prototype = new tmpClass();
};
The inherit method creates a temporary class with an empty constructor (so it doesn't matter if it is called because of inheritance). This new temporary class uses the same prototype as the specified super class. Then the prototype of the current class is overwritten with an instance of the temporary class. That's all.
Now we are able to extend the Person class in real OOP manner. Here is the code for the NickPerson class:
// Constructor for NickPerson class
function NickPerson(firstName, lastName, nickName)
{
// Call parent constructor
Person.call(this, firstName, lastName);
// Initialize new property
this.nickName = nickName;
}
// Derive NickPerson from Person
NickPerson.inherit(Person);
// Overwritten toString method
NickPerson.prototype.toString = function()
{
return this.firstName + " " + this.lastName
+ " (" + this.nickName + ")";
};
As you can see we call the inherit method as a static method of the previously created NickPerson class. Because all objects (even class objects) inherits all methods of the Object class the NickPerson class knows the inherit method. The advantage in using such a static method is that we don't have to specify the derived class as a parameter. The inherit method already knows the derived class because it is the class for which the method was called (so it's accessible with this). But you can also implement inherit as a simple function which takes the child class and the parent class as parameters instead if your prefer not touching the built-in Object class.
Call parent methods
For calling parent methods or the parent constructor we can use the call method which is defined by the built-in Function class. The example above already uses this feature to call the parent constructor. The first parameter of the call method must always be the current object instance (this) and the remaining parameters are the method parameters. Calling a parent method works nearly the same way as calling a parent constructor. The only difference is that you have to call the method on the prototype of the class and not directly on the class. Here is another example which shows how to call the toString method of the Person class from within the overwritten toString method of the NickPerson class:
// Overwritten toString method
NickPerson.prototype.toString = function()
{
return Person.prototype.toString.call(this) + " (" + this.nickName + ")";
};
Here are the complete scripts described in this article and a demo page which uses them:
Static properties and methods
By looking at all the unusual stuff which is needed to reach working inheritance it is nice to see that static properties and methods are quite easy and intuitive to use. A class (or better a function) is also some kind of object in JavaScript. So you can put properties and even functions into the class itself like this:
// Define a class
function MyClass()
{
// Initialize/Increase a static instance counter property
if (MyClass.counter === undefined)
{
MyClass.counter = 1;
}
else
{
MyClass.counter++;
}
}
MyClass.resetCounter = function()
{
MyClass.counter = 0;
};
var obj1 = new MyClass();
var obj2 = new MyClass();
document.writeln("Instances: " + MyClass.counter);
You can access the static properties inside static methods and instance methods exactly the same. Just use the class name as qualifier. Inside static methods you can also access them via the this qualifier because this is the class itself.