Impellium Software
 ProductsServicesCompanyContact Us 
 

Object-Oriented JavaScript

Table of Contents

Abstract

Object-oriented programming principles and conventions can be added to the practice of programming with JavaScript language. There is no doubt that the object-oriented programming practice can give certain benefits in the entire software lifecycle. That can be required when resolving challenges of relatively complex development. For example, a GUI framework can be expressed as a set of “classes” that fully define visual component hierarchy, i.e. inheriting common functionality from the common parent, redefining methods, etc. Developer's code becomes cleaner and better maintainable.

This article describes how you in JavaScript, which doesn't support object-oriented features, make up for all it's limitations by using some simple conventions, rules and tricks.

If you are familiar with standard features - skip the following chapter.

Standard Object-Oriented JavaScript Features

Custom javascript Object (we will call it class for simplicity) can be defined as a function:

function Component(name, data)
{
this._name = name;// public property

this._data = data;// public property


var someStaticData = null;// private static variable

this.prototype.setName = _setName;// public member function
this.prototype.getName = _getName;// public member function
this.prototype.setData = _setData;// public member function
this.prototype.getData = _getData;// public member function

// function implementation
function _setName(name)
{
this._name = name;
}
function _getName()
{
return this._name;
}
function _setData(data)
{
this._data = data;
}
function _getData()
{
return this._data;
} function someStaticFunction()
{
//some code here
}
}
As it can be seen from the example above, four public methods are defined on the Component class. The instance of the class can be created and methods can be called on the instance as follows:

var component = new Component();
component.setName("SomeName");
component.setData("some string data");

Also, the _name and the _data fields are the properties of the class. Unfortunately all properties of javascript classes can be accessed from outside - they are all public:

component._name = "SomeName";

It is different with variables that are declared inside the class (see someStaticData). They cannot be accesed from outside, they are private. So what they can be used for? Thay are static and these variables are shared between all instances of the class.
if method is not assigned to class's prototype (see above someStaticFunction) it is like a variable described above private static method - it shouldnot contain in it's implementation any class methods or properties.

Extended Object-Oriented Conventions

This chapter explains how you can using limited abilities of Javascript make it Object-oriented language. Also we would show some programming tricks that optimize performance.

Initializers

Looking at earlier Component class how can we make it reusable or implement first OO feature - inheritance? What if we define some function which we call Initializer that would take one parameter, class, and assign to it all required methods (we use suffix T for convenience):

function ComponentT(obj)
{
obj.prototype.setName = _setName;// public member function
obj.prototype.getName = _getName;// public member function
obj.prototype.setData = _setData;// public member function
obj.prototype.getData = _getData;// public member function

// function implementation
function _setName(name)
{
this._name = name;
}
function _getName()
{
return this._name;
}
function _setData(data)
{
this._data = data;
}
function _getData()
{
return this._data;
}
}

Please notice that instead of this in prototype assignment we use argument obj.
Then we remove all method declaration and implementation from Component class:

function Component(name, data)
{
this._name = name;// public property
this._data = data;// public property
}

With Initializer also comes big performance boost - try to create 100 class instances (put 10 methods inside the class constructor), then use Initializer for the same class, you will have second approach work 10 times faster because prototypes are assigned only once instead of doing it on every object creation. Here we come to our first OOP feature - INHERITANCE.

Inheritance


Now lets put this line of code:

ComponentT(Component);

What happened? When function called it assignes all methods inside ComponentT function to Component - Component inherits ComponentT.
This is not useful if you have just one Component, but if you need to extend it's functionality in let's say Control class:

function Control(parent)
{
this._parent = parent;// public property

}

Then we would call it like this:

ComponentT(Control);

Control class now inherits from Component.If you want to be able to inherit from particular class you should provide Initializer. So we do the same for Control class but now we remove previous line of code and put it inside Initializer:

ControlT(Control);

function ControlT(obj)
{
ComponentT(obj);

obj.prototype.setParent = _setParent;// public member function
obj.prototype.getParent = _getParent;// public member function
obj.prototype.paint = _paint;// public member function
function _setParent(parent)
{
this._parent = parent;
}
function _getParent()
{
return this._parent;
}
function paint()
{
// some code
}
}

Picture will be more clear (if you don't get it yet) when we create let's TextBox which would extend Control which extends Component:
function TextBox(parent, text)
{
this._text = text;
}

TextBoxT(TextBox);

function TextBoxT(obj)
{
ControlT(obj);

obj.prototype.setText = _setText;// public member function
obj.prototype.getText = _getText;// public member function
obj.prototype.paint = _paint;// public member function

function _setText(text)
{
this._text = text;
}
function _getText()
{
return this._text;
}
function paint()
{
// some code
}
}

That's not done yet, how about properties they are left uninherited.
This problem is solved by simple convention:
  • We remove all properties from inside class functions (Component, Control, Textbox).
  • Put one member function in Initializer of every class and call it initialize
  • Inside implementation of this function put corresponding properties
  • Call this function from inside each constructor function
Complete resulting code will be:
function Component(name, data)
{
this.initialize(name, data);
}
function Control(parent)
{
this.initialize(parent);
}
function TextBox(parent, text)
{
this.initialize(parent, text);
}
function ComponentT(obj)
{
obj.prototype.initialze = _initialze;// public member function
obj.prototype.setName = _setName;// public member function
obj.prototype.getName = _getName;// public member function
obj.prototype.setData = _setData;// public member function
obj.prototype.getData = _getData;// public member function

// function implementation
function _initialze(name)
{
this.setName(name);
}
// rest of implementation here
}
function ControlT(obj)
{
ComponentT(obj);

obj.prototype.initialze = _initialze;// public member function
obj.prototype.setParent = _setParent;// public member function
obj.prototype.getParent = _getParent;// public member function
obj.prototype.paint = _paint;// public member function

function _initialze(parent)
{
Component.prototype.initialze.call(this);
this.setParent(parent);
this.paint();
}
// rest of implementation here
}
function TextBoxT(obj)
{
ControlT(obj);

obj.prototype.initialze = _initialze;// public member function
obj.prototype.setText = _setText;// public member function
obj.prototype.getText = _getText;// public member function
obj.prototype.paint = _paint;// public member function

function _initialze(parent, text)
{
Control.prototype.initialze.call(this, parent);
this.setText(text);
}
// rest of implementation here
}

Now everything is inherited. Please notice this nifty Control.prototype.initialze.call(this, parent); in TextBoxT's initialze method, if you don't know, it calls Control's implementation of initialze method buy passing itself as a reference telling function that this inside that function would be actually passed TextBox instance. It also passes parent as a first parameter. This allows to invoke base class method. As you can see we come to the next OOP feature - POLYMORPHISM

Polymorphism

From example above notice that each of the classes in hierarchy has it's own initialze method, method is overwritten by subclass. So what happens if you have an instance of TextBox which extends Control is created calling initialze method which in turn calls Control's initialze method which calls paint method?
var txtName = new TextBox(parent, "John Smith");

Both classes have paint method but correct method of TextBox will be called - this is called POLYMORPHISM.

Static Methods

You can declare static methods in Javascript as follows:
  • It is declared as normal member function
  • In it's implementation you cannot use classes non-static properties or functions
  • It is called from outside using class constructor function: Control.prototype.foo();

Interfaces

Interfaces are created as classes with difference that they don't have properties and in their function implementation you throw an error - this forces developer to implement the method if interface is the class's hierarchy:
function BinderT(obj)
{
IBinder(obj);
obj.prototype.setControl = _setControl;// public member function
obj.prototype.getControl = _getControl;// public member function
}
function IBinder(obj)
{
obj.prototype.setControl = _setControl;// public member function
obj.prototype.getControl = _getControl;// public member function


function _setControl()
{
throw "Please implement setControl method.";
}
function _getControl()
{
throw "Please implement getControl method.";
}
}