OOP in JavaScript

Intro


In this article I will introduce you to some common design patterns in JavaScript, including patterns commonly seen in OOP languages, with some compare and contrast after each explanation when useful.

Background


There’s a common thread in the programming community that JavaScript isn’t a real language. After all, it doesn’t appear to support inheritance, data protection, or any of the other things programmers are used to seeing in modern programming languages.

It’s my opinion that this is a fallacy. I believe that people are so used to the “way things are done” that college courses teach you, that they simply reject the “new way” without giving it a chance. JavaScript is that new way, and it’s capable of nearly all the things that OOP languages are.

I will show you how to do those things and more in this post.

Basic Inheritance


In JavaScript, the concept of inheritance is implemented through the idea of shared “prototypes”. A prototype is exactly what it says. This is our definition of our object and what we believe it should do.

The prototype is set via the prototype property of an object, as such:

function Thing(){
}

Thing.prototype.doSomething = function(){
  console.log('I\'m doing something!');
};

If you’re new to JavaScript, your first question might be why a function has properties in the first place. In JavaScript everything is an object — ’nuff said.

Now, what does this have to do with inheritance? Well, if we want object SubThing to do something that object Thing already does, then we should say that Thing is SubThing’s prototype, based on what we said above. This is how we do that:

function SubThing(){
}

SubThing.prototype = new Thing();

This is basic inheritance in JavaScript. It works because the new keyword calls the function definition that follows it, then returns an object with a hidden [[prototype]] property based on the visible prototype property that we set.

Creating Instances in JavaScript


Creating instances in JavaScript is done in much the same way as any other language, but someone coming from a more classic OOP language may not understand where exactly the object to instantiate is here.

Any function definition can be used with the new keyword in JavaScript to initialize an object. Using one of our function definitions above, that would look like this:

var subThing = new SubThing();

As stated above, what this really does is create an object with a hidden [[protoype]] property based on the prototype we defined (in this case inherited from Thing).

Accessing Properties of an Object


So far, we’ve explained what a prototype is, and how to define properties on this prototype, but we haven’t actually shown how these properties are eventually used.

Any property access, specified with the dot operator (obj.property) or dictionary syntax (obj[‘property’]), will begin by looking at the objects direct properties, and then begin crawling up the prototype chain.

This protoype chain is the hidden [[prototype]] property set on the object we instantiated when using the new keyword.

It is called a chain, because it will check the current object’s prototype, then any inherited prototypes, in the order they were inherited.

As an example suppose we redefined SubThing this way:

function SubThing(){
}

SubThing.prototype = new Thing();
SubThing.prototype.doSomethingElse = function(){
  console.log('I\'m doing something else!');
};

var subThing = new SubThing();

Calling subThing.doSomethingElse() will call the method above, as the instance has the hidden [[prototype]] property we talked about before, created using new.

What does calling subThing.doSomething() do? It first looks at subThing’s prototype. Finding no property with this name, it looks for inherited prototypes. It will find the inherited Thing protoype and call the appropriate method. This is the final step in JavaScript inheritance.

Of course, inheritance of properties is only one aspect of OOP. Following is methods for implementing other common patterns.

Instance Properties


This section only exists to distinguish between static and instance properties in JavaScript. Inheriting instance properties works in exactly the same way as inheriting methods. Anything placed on the prototype essentially becomes treated as a direct property of any instances.

Static Properties


To create a static property, you simply add a property to the function definition itself. This property will be the same for all instances. This works because everything, even functions, are objects in JavaScript.

function Thing(){
}

Thing.property = 'STATIC! :D';

console.log(Thing.property); // logs 'STATIC! :D'

Unlike some OOP languages, however, the property must always be accessed and set on the function (class) itself. It’s instances do not have access to the property.

Private Data


Private variables are implemented as variables which are initialized in the constructor of the function (class definition).

These variables are scoped to the constructor and can only be accessed within the constructor.

Methods which will access private data are created inside the same constructor.

function Thing(){
  var private = "private";
  // this refers to the instance in this case
  this.setPrivate = function(value){
    private = value;
  };
  this.getPrivate = function(){
    return private;
  };
}

var thing = new Thing(); // calls constructor
console.log(thing.getPrivate()); // logs "private"
thing.setPrivate("exposed!");
console.log(thing.getPrivate()); // logs "exposed!";

Public Data


Public data is any data either declared as part of the prototype, or added to the instance during the constructor call.

function Thing(){
  this.public = "I'm public!";
}

var thing = new Thing();
console.log(thing.public); // Logs "I'm public!"

function Thing(){
}

Thing.prototype.public = "Me too!";

var thing = new Thing();
console.log(thing.public); // Logs "Me too!"

Bonus: Static Inheritance


Here’s something you can’t do in most classical OOP languages: static inheritance.

function Thing(){
}

Thing.static = "I'm static!";

function SubThing(){
}

SubThing.static = Thing.static;

It’s really that simple!

For methods, you can use the following pattern:

function Thing(){
}

Thing.static = "hi";

Thing.staticMethod = function(){
  // 'this' refers to the caller
  console.log(this.static);
};

Thing.staticMethod(); // logs "hi"

function SubThing(){
}

SubThing.static = "hi again!";

SubThing.staticMethod = Thing.staticMethod;

SubThing.staticMethod(); // logs "hi again!"

The reason this works is that “this” refers to the caller in JavaScript, so it uses the “static” property of the current caller, which is Thing, then SubThing, respectively.

Conclusion


Thanks for reading! Hope this helps anyone interested in writing maintainable code in JavaScript, and even more so that it helps people get rid of the mindset that JavaScript isn’t a “real” language and finally start treating it like one instead of writing spaghetti code!

I tried to write this all to work as-is, but please let me know if you find any errors.