JavaScript is an object-oriented language, but instead of being class-based like Java, C++ or C#, it’s prototype-based. This means that common object-oriented features like, for example, inheritance work a bit differently here. The prototype-based paradigm is often confusing at first especially for people that are used to class-based languages (i.e. almost everyone). In this first article, we’ll start simple and instead of going into the nitty-gritty details of prototypes, we will focus on a different approach often called the functional pattern.
Let’s overview what we are trying to achieve first. We hope to solve two problems in particular:
- Privacy – JavaScript objects don’t have private members by default. This means you can’t hide neither data nor methods. Sometimes this is not important, but in a larger application having privacy can greatly help simplify development
- Code reuse – objects in JavaScript inherit from other objects. We’ll see how to do this using the functional pattern
A Simple Example
For the sake of example we’ll be building a very simple stack data structure. Granted, implementing a stack in JavaScript isn’t very useful, but bare with me, it does make for a good example. We will also extend the stack object by adding logging functionality used for debugging purposes.
Let’s get started. In the most basic case we need an object, so we use an object literal to create it. We wrap everything in a simple function that we can call every time we need a new stack object. Note that this is a regular function, it’s not a constructor function and it shouldn’t be called with new
, so we name the function starting with a lowercase letter.
function stack() {
var that = {
items: [],
pop: function pop() {
return that.items.pop();
},
push: function push(item) {
that.items.push(item);
}
};
return that;
}
There is nothing special about using that
for the new object. It’s not part of the language like this
, but it’s a common convention. It helps avoid one particular problem with this
that we will see later, namely how this
is bound to the function. It doesn’t matter whether you define everything inside one big object literal or create an empty object and then add properties and methods to it. Maybe the latter makes for more readable code, but it’s really up to you:
function stack() {
var that = { };
that.items = [];
that.pop = function pop() {
return that.items.pop();
};
that.push = function push(item) {
that.items.push(item);
};
return that;
}
And of course using the stack is really simple:
// create a new stack object - note that we are not using the new keyword
var myStack = stack();
myStack.push(5);
myStack.push(7);
myStack.pop(); // returns 7
myStack.pop(); // returns 5
myStack.pop(); // returns undefined
Adding Privacy
One problem with this implementation is that we are having a stack, but the internal array is public. Anyone can alter it in anyway, or even replace it with something else that is not an array at all. Take a look:
var myStack = stack();
myStack.push(5);
myStack.items; // [5]
myStack.items = 'error';
myStack.push(7); // TypeError: Object error has no method 'pop'
To get around this we will take advantage of the fact that functions in JavaScript create a new scope, so everything we define inside the stack
function won’t be visible from the outside, essentially making it private. On the other hand, since the local variables are part of the closure of any method defined on the new object, the method will get access to the local variable. A common term for these methods, coined by Douglas Crockford, is “privileged method”. Closures are always way more clear with a code example:
function stack() {
var that = { },
items = []; // items is a local variable in this case
that.pop = function pop() {
// instead of that.items, we use items directly
return items.pop();
};
that.push = function push(item) {
// same as pop - we use items directly
items.push(item);
};
return that;
}
We define items
as a local variable and initialize it to an empty array. Obviously no one can modify it outside of thestack
function, but we can still access it from the push
and pop
methods to do our job. In this case push
and pop
are theprivileged methods. I should also add that any parameters from the stack
function can also act as a private variable, as long as we don’t expose it through the object that we return. The reason is exactly the same – parameters are scoped to the function.
var myStack = stack();
myStack.push(5);
myStack.items; // undefined
myStack.items = 'error';
myStack.push(7);
myStack.pop(); // still returns 7
myStack.pop(); // still returns 5
As you can see in our improved example, the items is not part of the stack object at all. Adding or modifying an items
property is really the same as adding any other property that is not part of the object and doesn’t affect the functionality of the stack. The pop
and push
methods still work as expected.
Inheritance
Now let’s look at some inheritance. Keep in mind that our goal is to reuse code andinheritance is not always the best approach to code reuse and in fact object composition is often better. At this point it’s almost obligatory to add the famous quote from the GOF book – “Favor object composition over class inheritance.”.
With this in mind let’s see what can we do about implementing the feature. We already said that in JavaScript there are no classes, so instead objects inherit from other objects. Let’s look at a particular example. Imagine we want to built another stack object that also has a name that is used for debugging purposes and we want to inherit from the stack object we already have. If we just keep in mind that we have to inherit from an object rather than from a class it becomes quite simple:
function namedStack(name) {
var that = stack();
that.getName = function getName() {
return name;
};
return that;
}
Instead of creating an empty object, we call the stack
function to create a new stack for us and then simply augment it with the new functionality. The name here will also be private since it’s a parameter, so we get that for free. If we want people to be able to rename our stack we can always add a set method or more simply add the name to the object directly likethat.name = name;
. In any case we have the option of keeping it private which is cool.
Note that this is also not a real inheritance in the sense that we are essentially adding functionality to an object, but it’s not related to the other object in any way. It has its own copy of all methods and properties.In the next part we’ll show a different approach using the JavaScript built-in prototype chain that doesn’t copy methods around, but has its own set of drawbacks as well.
But let’s return to the inheritance. We created a stack, added more methods to it — and Presto, we have some inheritance going on. How about overriding some methods?
Overriding and calling super methods
Well, if we want to “override” a method we can simply set it to a new function and that’s all – we don’t need any special mechanisms for that. However, if we want to still be able to call the parent object’s implementation, we have to save a reference to it. Unfortunately, this is a bit more complicated, mainly due to the way this
is bound to the function in JavaScript. Let’s start simple and ignore this
for now.
function namedStack(name) {
var that = stack(),
super_pop = that.pop,
super_push = that.push;
that.getName = function getName() {
return name;
};
that.pop = function pop() {
console.log('Popping from ' + name);
return super_pop();
};
that.push = function push(item) {
console.log('Pushing ' + item + ' into ' + name);
return super_push(item);
};
return that;
}
It’s actually pretty straightforward – we save a reference to the original methods, so we can call them later and then override them with our methods. This is easy and might actually work, but there is one potential problem, namely thethis
pointer inside super_pop
and super_push
will no longer be bound to the stack object, but rather to the global object, which in a browser is the DOM window. If we don’t use this
, it might actually work like this, but it’s a bit dangerous and we might want to ensure that this
is bound correctly just to be sure. The solution is to use call
instead of calling the function directly and supply the correct this
instance – super_pop.call(that)
andsuper_push.call(that, item)
. The first argument of call
is the object to use as this and the rest are the ‘normal’ function arguments. This fixes it for now, but the syntax is a bit awkward and we definitely don’t want to remember to call the super methods like this every time, so we might as well create a reusable function to help us. In “JavaScript: The Good Parts” Douglas Crockford suggest adding a superiour
method to the global object that looks roughly like this:
Object.prototype.superior = function (name) {
var that = this,
method = that[name];
return function () {
return method.apply(that, arguments);
};
};
apply
works just like call
, but we supply the rest of the arguments as an array instead of listing them and arguments
is an array-like object with all arguments supplied to the function. So we are creating a function that will call our desired function, which we specify by its name
. The wrapper function will then call our function with correctly bound this
instance and all arguments it was called with. If you don’t like the idea of augmenting object
, you can always define the function as a normal function and call it with two parameters – instance and function name. Let’s see how this will simplify our code:
function namedStack(name) {
var that = stack(),
super_pop = that.superior('pop'),
super_push = that.superior('push');
that.getName = function getName() {
return name;
};
that.pop = function pop() {
console.log('Popping from ' + name);
return super_pop();
};
that.push = function push(item) {
console.log('Pushing ' + item + ' into ' + name);
return super_push(item);
};
return that;
}
When we do that.superior('pop')
, what happens inside superior
is first that
is set to be equal to this
, which is our stack object also called that
(but don’t get confused about it, it doesn’t matter that the names are the same. This is just because we are using the same convention). Then method
is set to a reference to the stack’s function pop
and a new function is returned that will apply method (i.e. pop
) to the stack object using whatever arguments you supply. Let’s see an example of using the stack:
var myNamedStack = namedStack('my stack');
myNamedStack.getName(); // returns 'my stack'
myNamedStack.push(7);
> Pushing 7 into my stack
myNamedStack.push(5);
> Pushing 5 into my stack
myNamedStack.pop(); // returns 5
> Popping from my stack
Using protected data( to be continued )
Another thing you might expect to have as part of an inheritance mechanism is having the notion of protected methods and properties. While this can certainly be achieved (we are about to see how), it adds more complexity and you have to decide for yourself whether it’s worth it or not. One particular problem is that this time we’ll have to modify ourstack
function a bit. Namely, we’ll be adding a container for the protected data:
function stack(protectedData) {
var protectedData = protectedData || {},
that = { };
protectedData.items = [];
that.pop = function pop() {
return protectedData.items.pop();
};
that.push = function push(item) {
protectedData.items.push(item);
};
return that;
}
And then in our named stack function:
function namedStack(name, protectedData) {
var protectedData = protectedData || {},
that = stack(protectedData),
super_pop = that.superior('pop'),
super_push = that.superior('push');
/* we can access protectedData.items here */
that.logItems = function logItems() {
console.log(protectedData.items);
};
/* rest of implementation */
return that;
}
So what we do is we add another parameter to the parent stack function that will hold anything we decide to expose as a protected member and initialize it to an empty object in case the function wasn’t called from another function that wants to inherit or simply the caller doesn’t care about protected stuff. We later can access this object form our namedStack and of course we add a similar parameter to it in case someone wants to inherit it as well.
Last thoughts on inheritance
As a conclusion to the inheritance part I want to add that we must remember that inheritance is simply a means to a goal and the goal is code reuse. We shouldn’t be using it if it makes the code less clear or adds too much complexity. As mentioned before there is often a better approach. You have to decide for yourself how much of it you want to use. For me personally, the last bits with protected access go a bit too far and I’d rather skip it. In any case it’s good to know what is possible and what existing patterns there are.
Conclusion
Stay tuned for parts two and three of these series on object oriented JavaScript. In part two we talk about how theprototype works and in part three we look at a different approach to writing object-oriented JavaScript using theprototype-chain and constructor functions.
Feedback is always welcome, so please write a comment if there is anything you liked or didn’t like about this article. Thanks for reading!