JavaScript/OOP Primer
Object Oriented Programming (OOP) is a programming paradigm that uses "objects" to design applications and computer programs. It is based on several techniques, including inheritance, modularity, polymorphism, and encapsulation. It was not commonly used in mainstream software application development until the early 1990s. Many modern programming languages now support OOP.
If you don't understand what all these terms mean, that's where I come in!
Firstly, new programmers often look at all of this, understand it, and then think: "What's the Point!?", so, here's why you should use object oriented programming.
Object Oriented Programming is a very natural, easy and efficient way to make a program. In real life, we think of everything in terms of objects. Games lend themselves towards Object Oriented Programming as well, because of the amount of Objects in them - Sword, Shield, Window, Menu etc.
Using advanced OOP, it's very easy to extend and expand on other code. By inheriting objects, you don't need to reinvent the wheel and type out code all day. Less typing means you make less mistakes, which means your headaches go away.
Contents
Introduction
In the earlier days of programming, programs started at the beginning, finished at the end, and the only way you could possibly redirect program flow was to use a series of statements such as goto
to jump to another line in the program. For larger projects, this quickly became unworkable, as one would end up with a vast amount of goto
statements going everywhere, a condition known as spaghetti code, that was almost impossible to understand.
To remedy this, some very smart people invented procedural programming. Procedural programming is typically characterized by the use of user-defined subroutines and functions, that could be called upon by the program to perform a specific task or calculate a value. A subroutine does not return a value, whereas a function does (see the following example).
// JavaScript Example of Subroutines and Functions
function AddTwoNumbers (argument1, argument2) { //function
return argument1 + argument2;
}
function DisplaySumOfTwoNumbers (argument1, argument2) { //subroutine
Abort(argument1 + argument2);
}
//now we can see that we can call the function and it will return the value to the main program:
Abort(AddTwoNumbers(1,2)); // will return 3 to abort.
//The subroutine, if the program were to get this far (which it wouldn't) could be used to achieve the same task with:
DisplaySumOfTwoNumbers(1,2); // will abort with value 3
// End Example
This procedural programming is still very much in use today. Procedural languages include C, some BASIC variants, and more. Procedural language constructs are also available in most modern Object Oriented Languages such as C++.
In this procedural programming paradigm, large amounts of data would be stored in complex data structures, and subroutines and functions would manipulate that data.
Object Oriented programming takes this one step further, by, basically, integrating specific subroutines and functions into complex data structures.
Scope
Scope is a term used to describe whether certain variables, functions (and objects) are accessible from a particular point in a program. Loosely speaking, there are two major types:
- Global Scope - The identifier is available to all parts of the program
- Local Scope - The identifier is available to only specific parts of the program.
//JavaScript Example for Basic Scope
var myinteger = 1; // This variable can be accessed anywhere
function game() {
var mynextinteger = 2; //This variable can be accessed only within the game function
}
//End Example
Most of the time, if you are writing in purely procedural JavaScript, functions will be in the global scope, however, you can put functions within functions, and those sub-functions will only be accessible from within the function in which they are placed.
//JavaScript Example for Function Nesting
function game() {
function exitgame() {
//Some Stuff
Abort();
}
exitgame(); //This call will work
}
exitgame(); //This call will NOT work
//End Example
Instantiation and Prototyping
Now that we have the basics of procedural programming out of the way, we can move on to some basic Object Oriented Programming. An object (or class) is a structure that contains the following two elements:
- Properties (members) - Variables stored within the object that are accessible from anything in the same scope as the object.
- Methods - Subroutines or Functions that are part of the object, that can be accessed by anything in the same scope.
In JavaScript, and most languages, we must create a prototype (sometimes ambiguously called a class) of the object that describes the objects properties and methods. Once we create the prototype, we must make an instance of a particular prototype, that will form the object we intend to use. You can make more than one instance of a prototype (i.e multiple objects of the same type) and can also make arrays of instances and so on. This process of creating an instance is called Instantiation. Objects being independent of their prototype and of each other is called Encapsulation.
//JavaScript Example of a Prototype and Instantiation
function MyObject() { //prototype, not a function
this.property = 1; // creates a property called "property" and gives it an initial value of 1 for all instances of this object
this.method = function () {
//Do some thing here
}; //Here we have created a method within the object. These are like functions that can be accessed as members of the object.
}
//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject();
var MyObjectInstance2 = new MyObject();
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2; //sets the property of the second instance to 4
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance
Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 6
//End JavaScript Example
Note
We can also access the prototype of an object and add/remove to it outside the prototype definition. This is particularly handy for defining methods as nesting functions like above can get confusing and awkward.
//JavaScript Example of Further Prototyping
function MyObject() { //ignore the use of the word "function" here, it's not really a function.
this.property = 1; // creates a property called "property" and gives it an initial value of 1 for all instances of this object
}
MyObject.prototype.method = function () { //We are adding a method to the prototype after it has been defined.
//Do Something Here
}
//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject();
var MyObjectInstance2 = new MyObject();
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2; //sets the property of the second instance to 4
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance
Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 6
//End JavaScript Example
We can also make the object accept arguments to it's constructor prototype as shown:
//JavaScript Example of Further Prototyping
function MyObject(valueForProperty) { //ignore the use of the word "function" here, it's not really a function.
this.property = valueForProperty; // creates a property called "property" and gives it the value of the parameter when instantiated.
}
MyObject.prototype.method = function () { //We are adding a method to the prototype after it has been defined.
//Do Something Here
}
//Now that we've made a prototype, we can make a instances of the prototype as follows:
var MyObjectInstance1 = new MyObject(1); //Creates an instance with a property value of 1.
var MyObjectInstance2 = new MyObject(4); //Creates an instance with a property value of 4.
MyObjectInstance1.property = 2; //sets the property of the first instance to 2
MyObjectInstance2.property = MyObjectInstance2.property * 2; //sets the property of the second instance to 8
MyObjectInstance1.method(); //executes the method for the first instance
MyObjectInstance2.method(); //executes the method for the second instance
Abort(MyObjectInstance1.property + MyObjectInstance2.property); //Gives 10
//End JavaScript Example
Instantiation and prototyping is the first major step towards using Object Oriented Programming in a powerful and efficient manner.
Objects as Properties
You can make an object a property of another object. This is useful when your object requires sub-objects, for example, a hand
object might have five finger
objects.
We define Objects as Properties much like we define methods (because JavaScript uses function statements for both).
//JavaScript Example for defining Objects as properties of another object.
function MyObject() {
this.subobject = function () {
this.property = 1;
}
this.subobject.call(this.subobject);
}
//We can then instantiate and access as normal:
var a = new MyObject();
Abort(a.subobject.property); //Gives 1
The only difference, in fact, is this line:
this.subobject.call(this.subobject);
This line makes JavaScript execute the function so as to create the prototype. We use the call
function to specify that this
should apply to the subobject when within the subobject, not the parent object (which is what it does by default).
Inheritance
Inheritance means creating an object prototype that inherits all properties and methods from another prototype. You can use this to good effect - A talking window could inherit from a generic window class.
Before I launch into examples, some terminology may be useful: A subclass is a prototype that inherits from a superclass. These are sometimes called inheritor and inherent classes respectively.
You can make methods and properties in a subclass override methods and properties in the superclass. This lets you define a series of subclasses that have the same set of methods, but behave differently (basic polymorphism).
Below is a basic example of inheritance and overriding.
//JavaScript Example of Inheritance and Ovverriding
function superClass() {
this.bye = function () {
return "Bye from superClass";
}
this.hello = function () {
return "Hello from superClass ";
}
}
function subClass() {
this.inheritFrom = superClass; //Here we take on the properties and methods of superClass()
this.inheritFrom(); //This performs the inheritance
this.bye = function () {
return "Bye from subClass";
} //We have overridden the bye function of the superclass
}
var newClass = new subClass();
abort(newClass.hello() + newClass.bye()); //Will output: "Hello from SuperClass Bye from SubClass"
//End Example
Like simpler prototyping, you can also do this outside the prototype definition (although this is more unclear) as shown:
//JavaScript Example of Inheritance and Ovverriding with External Prototyping
function superClass() {
this.bye = function () {
return "Bye from superClass";
}
this.hello = function () {
return "Hello from superClass ";
}
}
function subClass() {
//Any stuff that isn't going to be overridden by the superclass can go here
}
subClass.prototype = new superClass(); //inheritance now done here
subClass.prototype.bye = function () {
return "Bye from subClass";
}
var newClass = new subClass();
abort(newClass.hello() + newClass.bye()); //Will output: "Hello from SuperClass Bye from SubClass"
//End Example
Often, that's all you'll need for simple OOP-based programs.
Polymorphism
Polymorphism describes the treatment of a set of objects as if they were another type of object (and have it compile/execute). It most commonly refers to objects that have methods of the same name and thus can be effectively treated as the same object in a loop. Usually, this means each object in question inherits from the same superclass, however this is not always the case.
Situation where Polymorphism/Inheritance is useful: You have one NPC superclass, and two different subclasses for two different NPCs. All three objects have a walk()
method. The superclass is prototyped with a random-walking script for the method, but you want to make one of the NPCs remain stationary. So you create an object hierarchy as shown:
//JavaScript Example of an Object Hierarchy
function NPC() {
this.walk = function () {
//Random Walking Script goes in here
}
}
function NPC_1() {
this.inheritFrom = NPC;
this.inheritFrom();
this.walk = function () {
//Remain stationary
}
}
function NPC_2() {
this.inheritFrom = NPC;
this.inheritFrom();
}
//End Example
Now, you have chosen to store all your NPC instances in an array, and, for some reason, in your script, you would like to have it instruct all NPC's to walk
. Because of polymorphism, you can treat all these different classes as the same, as you can see in the following example. This example also shows other useful things such as arrays of objects, and For...In loops.
//JavaScript example of Polymorphism
function NPC() {
this.walk = function () {
//Random Walking Script goes in here
}
}
function NPC_1() {
this.inheritFrom = NPC;
this.inheritFrom();
this.walk = function () {
//Remain stationary
}
}
function NPC_2() {
this.inheritFrom = NPC;
this.inheritFrom();
}
//Here we use a for...in structure to iterate through our array of objects
var NPCs = new Array();
NPCs[0] = new NPC_1();
NPCs[1] = new NPC_2();
for (x in NPCs)
{
objects[x].walk();
}
//End Example
In this example, because both NPC types have a "walk" function, you can treat them both as if they were instances of the NPC superclass, but have them perform different actions.
I hope you can now appreciate the power of inheritance and polymorphism.
Accessing the Superclass
One of the limitations of traditional JavaScript OOP is that you can't access the original methods that have since been overridden from within a subclass. One of the major uses of this is to add code to a function when overriding without removing the code that is already there. So, you can "patch in" additional code before or after the existing code from the superclass within a specific function.
To access the superclass (and therefore the methods), you need to use a little work-around. This little-work around requires you to use a "class Extension approach" to create objects, rather than the traditional JavaScript OOP. Kamatsu has created a library for this purpose called Spheritype, part of his Kamatsu's Class Library package.
I personally think this approach is cleaner, but it is less widely known and used.
Spheritype
Note
Credits
This article was (almost) entirely written by Kamatsu.
Cheers,
Kamatsu 13:10, 21 July 2007 (IST)