Object Oriented JavaScript

This article was originally published in VSJ, which is now part of Developer Fusion.

JavaScript and objects is possibly the most confusing topic in programming at the moment. The reason for the problem is a certain timidity with which objects were originally incorporated into JavaScript, and partly the flexibility of the language making it possible to implement missing features in many different ways.

We have looked at prototype inheritance before and now it’s time to look at the uneasy relationship between JavaScript and classical object-oriented practice. Notice that while there are aspects of JavaScript’s object-oriented use which are messy and perhaps even ugly, the whole thing turns out to be very usable. In short OOP and JavaScript do fit together.

The new operator

Before we look at some of the more creative ways that objects can be used it is worth just going over the basics.

You can create an object in JavaScript simply by creating a variable of type Object or Function, and you can add methods and properties simply by declarative use. For example:

myObject1=Object();
myObject1.x=1;
myObject1.y=2;

…creates a new object and adds two properties to it – x and y. You can achieve the same result with an object literal:

myObject2={x:3,y:4};

In both cases notice that we have created an object, i.e. an instance of a type, rather than a class, i.e. a type. If you only want a single instance of an object then this direct construction is fine and serves its purpose. However one of the joys of OOP is the ability to “stamp out” new instances of a class. To do this in JavaScript you need the new operator and the idea of a constructor function. A constructor function is just a standard function that is turned to a specific purpose. All a constructor does is define, and perhaps initialise, properties and methods which is exactly what any function does. However, a constructor makes use of this variable to determine the object that the properties and methods belong to, as in:

MyClass=function(a,b)
{
    this.x=a;
    this.y=b;
};

Following this when you use:

myObject1=new MyClass(1,2);

…this is set to myObject1 and the MyClass function is equivalent to:

MyClass=function(a,b)
{
    myObject1.x=a;
    myObject1.y=b;
};

That is, the MyClass function adds properties to myObject1 in this instance.

Nothing new

At this point we have a constructor-based approach to OOP but we don’t have to use it. The new operator is entirely redundant if you write your constructor functions in a slightly different way. Unfortunately there are a number of ways of doing this, but one of the simplest is to construct an object within the constructor and return this after customisation. That is, we make use of a class factory rather than a simple constructor. For example:

MyClass2=function(a,b)
{
    var temp={};
    temp.x=a;
    temp.y=b;
    return temp;
};

…creates a new object as an object literal, adds some properties to it and then returns it as the result of the function. Using this technique to create instances of the class doesn’t need the use of new:

MyObject3=MyClass2(7,8);

This approach to creating classes and objects is more flexible than the use of new as we shall see.

Inheritance

So far the OOP idea seems to be well implemented by JavaScript, but what about inheritance? Well you could go back to the idea of using the prototype property to implement inheritance, but trying to manipulate the prototype property is messy. It’s one of the things that is wrong with the definition of JavaScript.

One way to implement classical inheritance is to make use of the parent’s constructor function. To see this in action first we need a slightly more complicated class to try things out:

MyClass1=function(a,b)
{
    temp={};
    temp.x=a;
    temp.y=b;
    temp.show=function()
    {
    	alert(temp.x+","+temp.y);
    }
    return temp;
}

In this case we have two properties and a method. You can check that everything works using:

MyObject1=MyClass1(1,2);
MyObject1.show();
MyObject2=MyClass1(3,4);
MyObject2.show();

The two instances really are separate from each other and the show method displays the data stored in each instance.

Suppose we now want a MyClass2 that inherits all of the properties and methods from MyClass1. This turns out to be very simple:

MyClass2=function(a,b)
{
    var temp=MyClass1(a,b);
    return temp;
}

All we do is use the MyClass1 constructor to initialise the temp variable and then return it. This is very similar to the way C++ works by calling parent constructors. You can also elaborate the basic idea of non-prototype inheritance by storing the name of the parent class or using it within the constructor. Any objects created using the new class have all of the properties and methods of the parent class, i.e. MyClass1, and you can write:

MyObject1=MyClass2(1,2);
MyObject1.show();

If you want to add to the new class you can do it in the obvious way by defining new methods and properties. For example to add a new method to MyClass2 you would simply write:

MyClass2=function(a,b)
{
    var temp=MyClass1(a,b);
    temp.add=function()
    {
    	return temp.x+temp.y;
    }
    return temp;
}

Now you can use the add method in MyClass2 but, of course, not in MyClass1:

MyObject1=MyClass2(1,2);
MyObject1.show();
alert(MyObject1.add());

If you want to override an existing method you simply redefine it:

MyClass2=function(a,b)
{
    var temp=MyClass1(a,b);
    temp.add=function()
    {
    	return temp.x+temp.y;
    }
    temp.show=function()
    {
    	alert("This is the content
    		of x="+temp.x);
    	alert("This is the content
    		of y="+temp.y);
    }
    return temp;
}

Now if you call show it will print something more informative. There is no distinction between overriding methods and overriding virtual methods because there are no virtual methods and, as JavaScript isn’t strongly typed, there is no real need for them or for polymorphism.

This is all very easy but there are one or two subtle points. How for example do you call the method in the super class within the overriding method? The solution to this problem is to simply store a copy of the original method. For example:

MyClass2=function(a,b)
{
    var temp=MyClass1(a,b);
    temp.supershow=temp.show;
    temp.add=function()
    {
    	return temp.x+temp.y;
    }
    temp.show=function()
    {
    	alert("This is the content of x="+temp.x);
    	alert("This is the content of y="+temp.y);
    	temp.supershow();
    }
    return temp;
}

Now when you use the new show method the original method is called as the last action. Notice that you can’t extend this method to the entire super class as in:

var temp=MyClass1(a,b);
temp.super=temp;

…and then call a parent method using:

temp.super.show();

This doesn’t work because temp.super is a reference to the same object that temp references and so any changes to the methods and properties of temp are also made to the object that temp.super references. There is no easy way to keep the original super class ready with all its methods to be called, and saving each one you override is the only sensible thing to do.

A complete copy of the class and any inherited class is made everything you instantiate a new object. This might slow things down and use up more memory that is strictly necessary. It isn’t a problem, however, if the number of objects you are creating is small. If you do want to create a class that you intend to instantiate thousands of copies of then you need to make use of prototype inheritance which doesn’t create copies.

Encapsulation

Finally we need to examine the other main strand of OOP – encapsulation. JavaScript doesn’t have good facilities for creating private variables and generally the best that we can do is to rely on function scope, i.e. variables local to a given function. There is a well-known technique of using an object to provide a namespace, but this isn’t really the same thing as a private variable. For example, if you want to make sure that all of the variables that you create don’t clash with any variables created elsewhere in the program you can create an object:

myNameSpace={};

…and from this point on everything you create is internal to the object, e.g.:

myNameSpace.x=10;
myNameSpace.y=20;

…and so on. This stops x and y colliding with any x and y defined elsewhere and this is useful, but it still doesn’t stop access to your particular x and y as long as the namespace is used.

To create truly private variables we have to resort to making use of that ever surprising facility – the closure. The reason why the usefulness of the closure is surprising is simply that when you first encounter it it seems to be abstract and academic not something practical. In this case to create a class with two private fields, x and y say, we need to create the two variables within an outer function and the function that actually creates the class in an inner function. In this way the outer function can terminate but anything created by the inner function will still have access to the local variables belonging to the outer function – this is what closure is all about. Of course any function or object not created by the inner function won’t be able to see the outer variables because the function that created them is long gone and they are out of scope. It sounds easy but there are some interesting tricks that make it all so much easier.

The constructor starts off in the usual way but this time the two private variables are declared as local variables to the MyClass1 function:

MyClass1=function(a,b)
{
    var x;
    var y;

Thus the variables x and y are not available to any other function and in the normal way of things would go out of scope as soon as MyClass1 terminates. However, we are now going to define an inner function which uses the two private variables and creates the object that the constructor will eventually pass back:

   init= function(a,b)
    {
    	x=a;
    	y=b;
    	var temp={};
    	temp.show=function()
    	{
    		alert(x+","+y);
    	}
    	return temp;
    };

Once all of this is complete all that remains is to call the inner function and return its result:

   return init(a,b);
}

If you try this out you will discover that you can instantiate objects in the usual way and the values to which you initialise x and y remain accessible even though the constructor has completed.

There is a slightly better way to write this constructor but it is arguable that the form given above is easier to understand. Instead of explicitly calling the inner function we can do the job as part of the return:

MyClass1=function(a,b)
{
    var x;
    var y;
    return function(a,b)
    {
    	x=a;
    	y=b;
    	var temp={};
    	temp.show=function()
    	{
    		alert(x+","+y);
    	}
    	return temp;
    }(a,b);
}

Notice how the anonymous function is defined within the return outer function’s return statement and notice the way it is called to by the final (a,b) to produce a result for the return to pass back.

If you need to provide controlled access to private objects you can write set and get methods. For example, to allow write access to x and y simply write:

MyClass1=function(a,b)
{
    var x;
    var y;
    return 	function(a,b)
    {
    	x=a;
    	y=b;
    	var temp={};
    	temp.show=function()
    	{
    		alert(x+","+y);
    	}
    	temp.setx=function(a)
    	{
    		x=a;
    	}
    	temp.sety=function(b)
    	{
    		y=b;
    	}
    	return temp;
    }(a,b);
}

There are lots of other approaches to OOP in JavaScript but they all revolve around the sort of techniques described above. Do we really need a revamp of JavaScript to make it truly object oriented? Probably not.


Ian Elliot is a development programmer with I/O Workshops Ltd, a consultancy which solves real-world problems for clients with widely varying requirements.

You might also like...

Comments

About the author

Ian Elliot United Kingdom

Ian Elliot is a development programmer with I/O Workshops Ltd, a consultancy which solves real-world problems for clients with widely varying requirements.

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“C++ : Where friends have access to your private members.” - Gavin Russell Baker