Javascript Prototypes

Classes, Properties, Methods and Ships

Javascript is an object oriented (OO) programming language. Many object oriented programming languages, such as Java and ActionScript, are class based.

Titanic_plan

A 'class' acts as a blueprint for objects or instances created from the blueprint. We may, for example, have a class 'Ship' that defines the basic characteristics of a Ship - its crew, displacement and speed for example. These would be 'properties' of a Ship created from that class. The blueprint/class would also outline what the object it creates can do - its methods - so our Ship can moveForward(), tootHorn(), sink(). In short properties are adjectives/descriptive, whilst methods are doing words/verbs.

A class is abstract, it doesn't exist until you instantiate an object - build a ship, from the blueprint. So at the shipyard the blueprint in the company office is the class, but as a ship built from the blueprint rolls down the slipway it is very much an object. In many languages the process of instantiating an object is done with the new keyword ie:

var titanic = new Ship();

When we create something from the class, we have physically created something and this is an object. An object can have properties that describe it and methods that prescribe what it can do. So our ship has properties of crew, displacement and speed, and methods of moveForward(), tootHorn() and sink().

The joy of classes is that we can have many objects based on it. For example did you know that titanic had a sister ship called 'Olympic'.

var olympic = new Ship();

... and here they both are:

Olympic_and_Titanic

Note: You may be interested to know that shipbuilding and many other industries use the concept of 'classes'. For example Titanic and Olympic were both 'Olympic' class liners and indeed there was a third class member Britannic.

Conventions

You'll notice some conventions here. Class names are capitalized ie Ship, whilst property names are lowercase. Methods are often verbNoun - what is known as camelCasing where we start with a lowercase word then have a capitalized second word. Methods will normally have parenthesis - brackets () - as methods often takes values such as tootHorn('loudly') or moveForward(500).

Inheritance

A key benefit of object oriented programming languages is that of inheritance. We already have our Ship class but there might be more subtle classifications of Ship such as Liner, WarShip, Ferry and OilTanker. These will all have the same 'base' characteristics of the Ship class, so there is no need to indicate that each will have a crew, tootable horn and might sink. These properties and methods can be 'inherited' from Ship.

But Javascript is class-less

Unlike the accommodation on the Titantic - Javascript is 'class-less'. Javascript does not have a class keyword like for example ActionScript. Javascript is a prototype based programming language. Instead of 'classes', in Javascript objects can inherit properties and methods from each other by the cloning of an existing object known as a prototype. Whenever you create an object in Javascript such as an array or string - these are objects based on a prototype. You can also create your own objects and have them act as prototypes and even extend the prototype of built-in objects. Let's take a look.

Prototypes and Objects

In Javascript we can create Objects using a function like so:

function Ship(c, d, s){
 this.crew = c;
 this.displacement = d;
 this.speed = s;  
}
var titanic = new Ship(892, 52310, 24);
document.getElementById('display').innerHTML = titanic;

JSFIDDLE Demo

In the above example the output is the rather cryptic object object. This is because to see anything meaningful we need to loop through the object properties.

Using a for ... in loop we can make more sense of the values:

function Ship(c, d, s){
 this.crew = c;
 this.displacement = d;
 this.speed = s;   
}
var titanic = new Ship(892, 52310, 24);
for (var prop in titanic) {
     if(titanic.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += titanic[prop];
        document.getElementById('display').innerHTML += "<br>";
     }
}

JSFIDDLE Demo

Tip: Notice the use of the capitalized function name to indicate that this function is used to create an object. This kind of function is known as a constructor.

Inheritance

ark_royal

Now if we extend this example by creating a new 'classification' of Ship ie 'Warship'.

function Ship(c, d, s, b){
 this.crew = c;
 this.displacement = d;
 this.speed = s; 
 this.built = b; 
}
function WarShip(c, d, s, g){
 this.crew = c;
 this.displacement = d;
 this.speed = s;   
 this.guns = g;
}
WarShip.prototype = new Ship();
var arcRoyal = new WarShip(685, 22000, 30, 5);

for (var prop in arcRoyal) {
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += arcRoyal[prop];
        document.getElementById('display').innerHTML += "<br>";
}

We now have a Ship and WarShip constructor functions that can be used to create objects. We also create a new WarShip using the line:

WarShip.prototype = new Ship();

This makes Ship the base or prototype of the WarShip object. We then create a new object from WarShip with:

var arcRoyal = new WarShip(685, 22000, 30, 5);

Notice however, that when we loop through the properties with a for ... in we have an undefined value for the property built.

JSFIDDLE Demo

This is because of something called the prototype chain. The for ... in is looking at all the properties of the object included those that it is based upon. By 'based upon' what we really mean is the Prototype of the object. Therefore as Ship has a property of built, and as it is the Prototype of WarShip, the Ship property gets picked up and happens to be undefined.

To avoid this we use hasOwnProperty that ensures only properties that belong to the current object are returned. Therefore in the for ... in the code is changed to:

for (var prop in arcRoyal) {
     if(arcRoyal.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += arcRoyal[prop];
        document.getElementById('display').innerHTML += "<br>";
     }
}

JSFIDDLE Demo

Extending an object with prototype

The prototype property is used in the example below to extend the Ship object with a new method of setSpeed(). This new method is then available to all objects that build on Ship in this case both titanic and olympic.

function Ship(c, d, s, b){
 this.crew = c;
 this.displacement = d;
 this.speed = s; 
 this.built = b; 
}
Ship.prototype.setSpeed = function (spd) {
    this.speed = spd;
};
var titanic = new Ship(892, 52310, 24, 1911);
titanic.setSpeed(48);
var olympic = new Ship(900, 52607, 24, 1910);
olympic.setSpeed(65);
for (var prop in titanic) {
     if(titanic.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += titanic[prop];
        document.getElementById('display').innerHTML += "<br>";
     }
}
for (var prop in olympic) {
     if(olympic.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += olympic[prop];
        document.getElementById('display').innerHTML += "<br>";
    }
}

JSFIDDLE Demo

We could also add a new property to the prototype with:

Ship.prototype.builder = "Harland and Wolff, Belfast";

As both ships were built at Harland and Wolff in Belfast we would like to add this to the prototype. If we add this to our example:

function Ship(c, d, s, b){
 this.crew = c;
 this.displacement = d;
 this.speed = s; 
 this.built = b; 
}
Ship.prototype.setSpeed = function (spd) {
    this.speed = spd;
};
var titanic = new Ship(892, 52310, 24, 1911);
titanic.setSpeed(48);
var olympic = new Ship(900, 52607, 24, 1910);
olympic.setSpeed(65);
Ship.prototype.builder = "Harland and Wolff, Belfast";
for (var prop in titanic) {
     if(titanic.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += titanic[prop];
        document.getElementById('display').innerHTML += "<br>";
     }
}
for (var prop in olympic) {
    if(olympic.hasOwnProperty(prop)){
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += olympic[prop];
        document.getElementById('display').innerHTML += "<br>";
    }
}

JSFIDDLE Demo

We might expect to see the builder property listed but it isn't. This is because of hasOwnProperty is preventing both of our ships from moving up the prototype chain to the Ship class. All we see is the properties of titanic and olympic directly. So if we remove hasOwnProperty then we will see our new property.

function Ship(c, d, s, b){
 this.crew = c;
 this.displacement = d;
 this.speed = s; 
 this.built = b; 
}
Ship.prototype.setSpeed = function (spd) {
    this.speed = spd;
};
var titanic = new Ship(892, 52310, 24, 1911);
titanic.setSpeed(48);
var olympic = new Ship(900, 52607, 24, 1910);
olympic.setSpeed(65);
Ship.prototype.builder = "Harland and Wolff, Belfast";
for (var prop in titanic) {
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += titanic[prop];
        document.getElementById('display').innerHTML += "<br>";
}
for (var prop in olympic) {
        document.getElementById('display').innerHTML += prop;
        document.getElementById('display').innerHTML += ": ";
        document.getElementById('display').innerHTML += olympic[prop];
        document.getElementById('display').innerHTML += "<br>";
}

JSFIDDLE Demo

Extending Built-in Functions

By referencing the prototype of a built-in function we can extend their functionality. So keeping the nautical theme we can add a new method to String as follows:

String.prototype.pirateSpeak = function(){
	return this + ", me hearties.";	
};
var testString = "Hello";
testString = testString.pirateSpeak();

The above create a new method for String objects called pirateSpeak(). This can be referenced whenever we have a string in the same way as built-in String methods like replace(). The this keyword inside the method refers to the current objects value.

JSFIDDLE Demo

The 'non-standard' __proto__

The strangely typed __proto__ is an internal property of an object, pointing to its prototype. As such it can be used to point at the prototype via the instance. Take the following example:

var testString = "Hello";
var moreStrings = "Goodbye";
moreStrings.__proto__.pirateSpeak = function(){
	return this + ", me hearties.";	
};
// same as //
//String.prototype.pirateSpeak = function(){
//	return this + ", me hearties.";	
//};
testString = testString.pirateSpeak();
moreStrings = moreStrings.pirateSpeak();

JSFIDDLE Demo

Life does get complicated with Prototypes you might want to dive deeper here: