Classical Objects in JavaScript

I’ve recently found myself needing to do more with JavaScript than I have in the past and I thought a good way to understand some of the more interesting features of the language would be to attempt implement Java/C# style classes and inheritance. This has obviously been done before with various JavaScript libraries and frameworks such as Mootools, but I wanted to actually understand what was going on. With that in mind, this post is not about the best way to implement classes, it’s simply about what is happening when you do.

Java vs JavaScript

The big challenge to trying to copy what Java does is that Java and JavaScript are two very different languages. Java is a classic OOP language and all the features I’m trying to emulate are a core part of it. JavaScript has objects but they’re little more than hash tables and so everything is dynamically typed.

My ultimate goal is to implement an object hierarchy in JavaScript such as this Java sample. The features I’m aiming to emulate are:

  • Member functions
  • Public and private member variables
  • Public and private statics
  • Class extending

JavaScript Objects

And to get stuck in, here’s the JavaScript implementation I came up with:

ObjectA = (function(){

	var privateStatic = "private static";

	function make(x, y) {
		this.publicMember = x;
		var privateMember = y;

		this.setPrivate = function(v) {
			privateMember = v;
		}
		this.getPrivate = function() {
			return privateMember;
		}

		this.calc = function() {
			return this.publicMember + privateMember;
		}
	};

	make.prototype.setPublicStatic = function(v) {
		make.prototype.publicStatic = v;
	};

	make.prototype.setPrivateStatic = function(v) {
		privateStatic = v;
	};
	make.prototype.getPrivateStatic = function() {
		return privateStatic;
	};

	make.prototype.publicStatic = "public static";

	return make;

})();

ObjectB = (function(){

	function make(x, y, z) {
		ObjectA.call(this, x, y);
		var privateSubMember = z;

		var __calc = this.calc;
		this.calc = function() {
			return (__calc.call(this) * z) + this.publicMember;
		}
	}
	make.prototype = new ObjectA();
	make.prototype.constructor = make;

	return make;

})();

I’ve created a jsFiddle for the above along with some tests here. The first thing I should point out is that although I achieved my goals, I haven’t necessarily done it in the best way. The public static variable is almost certainly not done well, but I’ll go into that later.

Constructors

When implementing objects like this the the classes are actually functions and those functions also double up as the constructor. When you call new MyObject(); the interpreter allocates a new empty object and then passes it to the function you’ve specified for initialisation. In the code above, I’ve actually declared my constructors as functioned called make() within an anonymous function which returns the constructor.

Member Functions

This is probably the easiest thing to achieve as functions are first-class objects in JavaScript. This means that they can be passed around as data and assigned to other objects in the same way as any other data type. In my above code, I attach functions to my objects in two different ways. The first is by assigning it within the constructor:

MyObject = (function(){

	function make() {
		this.doStuff = function() {
			// Stuff happens here...
		}
	}

	return make;

})();

This approach will allocate a new instance of the function and store it directly within the constructed object each time the constructor is called. This may seem wasteful, but it’s the only way to access private members within the object.

The second way for defining member functions is by assigning the function to the constructors prototype:

MyObject = (function(){

	function make() {
	}

	make.prototype.doStuff = function() {
		// Do stuff here...
	}

	return make;

})();

The prototype member is a dictionary/hash table that is shared by all instances of the same object type. If a member isn’t found directly within the object instance, the prototype will be searched. Assigning functions to the prototype will cause only one instance of the function to be allocated per class, but these functions are only able to access public members of your object.

If you look back at my sample JavaScript above, you can see where I’ve used the two different types of function definition where I’m accessing variables with different access levels.

Public Member Variables

These are defined by simply assigning them to the this object within the constructior:

MyObject = (function(){

	function make() {
		this.member = 123;
	}

	return make;

})();

The JavaScript language has no syntax for public or private access levels so all members of an object are publicly accessible. As such, there’s not much more to say about public members.

Private Member Variables

This is where things start to get interesting. As I mentioned previously, JavaScript has no concept of public or private variables so we have to fake it using closures. Closures are functions that are able to access variables that while not global, are accessible within the scope that the function way originally defined. That probably didn’t make much sense, so here’s an example:

MyObject = (function(){

	function make() {
		var private = 123;

		this.doStuff = function() {
			return private * 2;
		}
	}

	return make;

})();

The variable private has been defined locally within the constructor which would normally mean that I couldn’t be accessed from outside the constructor, it’s private if you will. However, the function doStuff() was also defined within the constructor and so has access to the same scope including the local variable private.

Public Static Variables

In the code above, I’ve implemented public statics by assigning them to the constructors prototype. I did this so that I could access the statics in a syntactically similar way to Java. However, it has the very big problem in that if I did the following:

var o = new ObjectA();
o.publicStatic = "New Value";

The static variable wouldn’t be updated, instead a new member called “publicStatic” would be assigned to the object instance o.

A much better way to implement statics would have been for me to assign them as members of the constructor itself:

MyObject = (function(){

	function make() {
	}

	make.static = 123;

	return make;

})();

document.write(MyObject.static);

Each time I wanted to access the static, I would have to explicitly reference the class that the static belonged to which is not a requirement of Java and C#, but it would be clear exactly what I was doing which is no bad thing.

Private Static Variables

Back to closures again to simulate private variables. This time the static variables are local variables in the anonymous function scope.

MyObject = (function(){

	var private = 123;

	function make() {
	}

	make.prototype.getPrivate = function() {
		return private;
	}

	return make;

})();

This is the whole reason for using anonymous functions for defining classes. If you didn’t need private static functions, then you could implement everything in this post without using anonymous functions. Depending on how you feel, that might be a good thing or a bad thing.

Class Extending

By setting the constructors prototype to a new instance of the class you want to extend, you are able to simulate inheritance as you know it in other languages. Because the prototype is searched if a requested member isn’t found on the object instanced being referenced, it has the effect of exposing all all the super class members to the new class.

ObjectA = (function(){
    function make() {
    }

    make.prototype.hello = function() {
        document.write("Hello!");
    }

    return make;
})();

ObjectB = (function(){
    function make() {
    }

    make.prototype = new ObjectA();
    make.prototype.constructor = make;

    return make;
})();

var o = new ObjectB();
o.hello();

By creating a new instance of ObjectA and assigning it to the prototype, it means that we can add new items to the prototype without affecting the original ObjectA class or any other classes that extend it. However, as we’ve replaced our original prototype with a copy from another class, the constructor needs to be set back to the correct one again. constructor is a standard property that contains a reference to the function used to construct the object and so we should always make sure it points to the correct function.

Obviously, any additions you want to make to the prototype need to happen after you’ve set the super class!

Calling the Super Class Constructor

Generally, you should also call the super class’s constructor so that it is set up correctly in your new objects. In Java and C#, the default constructor will be called for you automatically so you often don’t need to worry about this but you must always do this explicitly in JavaScript.

ObjectA = (function(){
    function make(message) {
        this.message = message;
    }

    make.prototype.hello = function() {
        document.write(this.message);
    }

    return make;
})();

ObjectB = (function(){
    function make() {
        ObjectA.call(this, "Hello World!");
    }

    make.prototype = new ObjectA();
    make.prototype.constructor = make;

    return make;
})();

var o = new ObjectB();
o.hello(o);

You’ll notice that I’ve used the call() method to invoke the super class constructor. This is because the super class constructor isn’t being called as a member of any object and so the this pointer won’t be what you expect.

Here is a jsFiddle in which the super constructor is called directly. As you can see, when the ObjectA constructor is called directly from the ObjectB constructor, the this reference is actually the browser window. Using the call() method allows us to specify the object instance to use for this when calling the constructor.

This is another down side to attempting to emulate OOP in JavaScript. The call() method is similar to MethodInfo.Invoke() in other languages and so is actually much slower than calling methods directly. If you’re creating a lot of objects frequently and you need your code to run quickly, you don’t want to be calling super class constructors. Mind you, if you want your code to run quickly, you probably won’t be dynamically creating a large number of objects at run time.

Overriding Super Class Methods

The last thing to tackle is overriding super class methods. This is done by storing a copy of the super function and then overriding it with a closure that calls your stored copy.

ObjectA = (function(){

    function make() {
    }

    make.prototype.calc = function(x, y) {
        return x + y;
    }

    return make;
})();

ObjectB = (function(){
    function make() {

        var _calc = this.calc;
        this.calc = function(x, y) {
            return _calc.call(this, x, y) * 2;
        }

    }

    make.prototype = new ObjectA();
    make.prototype.constructor = make;

    return make;
})();

var o = new ObjectB();
document.write(o.calc(1, 2));

The reason for using a closure like this rather than simply accessing your class’s prototype is because you may not know if the super function is a closure or attached to the prototype. This way reduces the requirement for the sub class to know the exact implementation details of the super class and therefore reducing maintenance overheads if the super class implementation changes without an interface change.

You also need to use the call() method of the super function again in order to ensure the this pointer is set correctly. You will need to keep the overheads of overriding super class methods in mind if you need to implement fast code.

Conclusions

Although it is certainly possible to emulate classical OOP in JavaScript, in my opinions the overheads of doing so won’t always be worth it. Your code will be more complex and will have less optimal code paths unless you’re willing to trade maintainability and implementation isolation. I can understand the desire for someone to use a familiar programming style but I would question the need to use classical OOP in JavaScript. You can’t achieve an exact copy of what’s available in Java or C# so it’s important to know how it differs when you simulate it.

JavaScript is a great language and it feels like you’re doing it a bit of a disservice if you try to blindly make it act like another language it wasn’t meant to be.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: