Object Oriented Programming with JavaScript

No, you don't need any frameworks

Posted by Heikki Kupiainen/Oppikone on 2016-11-14
There has been a heresy around for a while suggesting that you cannot create good object oriented code in JavaScript without using either JavaScript's awkward native way of OOP called prototyping or non-JavaScript extensions such as TypeScript, CoffeeScript or other variants that compile into JavaScript. When you use those frameworks, you are not programming plain JavaScript but rather creating files with a new syntax. Those files are then compiled into true JavaScript.

EcmaScript 6 (ES6) will be a great improvement to JavaScript bringing some nice long-awaited features. Unluckily, at the time I am writing this article, ES6 support is not yet widespread enough. There are yet too many browsers around without ES6 support. But if you want to write apps that work in most browsers, you should wait a bit more before jumping onto ES6 train. But that should not prevent anybody from writing good object oriented code. I'll show you how!

Each framework has its advantage of course but you do not need to use TypeScript or any other framework to create effective object oriented code in JavaScript. With a little imagination, it is possible to create some very good object oriented code with plain good old non-falsified non-distorted JavaScript. In the following I'll show my way to code OOP in JavaScript.

Static Methods and Instance Methods

Anyone who knows Java is familiar with concepts of static methods and instance methods. Officially, there is no equivalent for these concepts in JavaScript. But pragmatically, you can have something very similar in JavaScript - without any third party extensions. Let us create a class in plain old JavaScript. We want to create an abstract class called Car to represent all possible man made transportation tools.

var Car = {}

That is our Car class! Now, let us add a static method to Car class telling the purpose of what "Car" means.


Car.getPurpose = function() {
   return "Cars are designed to transport items or living things.";
}

Of course, if you are lazy like me sometimes, you can combine these two declarations, having:


var Car = {
   getPurpose: function() {
      return "Cars are designed to transport items or living things.";
   }
};

The method invocation for our new method looks like this:

Car.getPurpose();

Creating Instances of a Class

Now, it is nice to have a Car class but if we really want to do OOP we will want to create instances of the Car class. That can be done by creating a constructor method for Car. This is how I do it:
var Car = {
   //a class method
   getPurpose: function() {
      return "Cars are designed...";
   }

   //a constructor for creating instances of Car
   new: function() {
      return {}
   }
};

Now, the constructor method that we created returns an empty object equivalent to Java's hash map or C#'s dictionary. Of course, an empty object like that doesn't convince us too much about its resemblance to a car - it could be anything, really. So let's decide that any car must have an instance method to tell the color of that very vehicle. Every car has at least a color, right?

In remembrance of first mass produced cars, we will decide that any vehicle's default color is black unless otherwise instructed. Therefore, we modify the constructor in a way that it returns a vehicle object having a method for getting its color:

var Car = {
   //a class method
   getPurpose: function() {
      return "Cars are designed...";
   }

   //a constructor for creating instances of Car
   new: function() {
      return {
          getColor: function() { return "black"; }
      }
   }
};

Now, the invocation for creating a new instance of vehicle would look like this:

var myNewCar = Car.new();

And getting the color of the newly created vehicle instance is easy:

var color = myNewCar.getColor();

Well, we might get bored with black color at some point. What we really want is to create different kinds of instances with different properties. Cars with different colors! We can achieve this diversity by modifying the constructor method so that we can pass the color as a parameter value for each vehicle we create:

var Car = {

   //a constructor for creating instances of Car
   new: function(color) {
      return {
         color: color,
         getColor: function(){
            return this.color;
          }
      }
   }
};

The modifications shown above added a color parameter to the constructor method of Car class. The constructor returns a new object that has one variable color and a method for getting the value of that variable. Now, let us create a blue vehicle:

var myBlueCar = Car.new("blue");

And for convenience, let us get the color of our vehicle:

var color = myBlueCar.getColor();

Needless to say, the color variable has now value "blue".

Inheritance In JavaScript

Do you remember, I just promised to show a great way to create awesome object oriented code in JavaScript without using any framework at all? Well, I lied! We do need a framework... Our framework is a one-liner and it comes here:

for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }

Feel free to copy-paste this awesome super modern OOP framework into your app! If you find it cool you can even wrap it into a reusable function:

function extend(obj1,obj2) {
   for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
   return obj1;
}

With this fantastic open source framework I just gave away for free it is possible to do some serious OOP in JavaScript. Namely, we can extend classes to create new classes that inherit features of their ancestor class. If you need a name for this framework let's call it AwesomenessJS. Now let's use our framework to try out inheritance. We'll create a very certain type of car, a road train.

When talking about road trains, which are in deed very large trucks, it is understable that the number of wheels is a significant attribute. If you know how many wheels a road train has you will have a picture about what kind of a truck we are talking about.

var RoadTrain = {
    new: function(color,wheelCount) {
       return extend(Car.new(color), {
          wheelCount: wheelCount,
          getWheelCount: function() {
             return this.wheelCount;
          }
       });
    }
};

The code described above enables us to construct road trains of variable colors and wheels, for instance:

var greenHugeRoadTrain = RoadTrain.new("green",18);

We can check the number of wheels of our road train anytime:

var wheelCount = greenHugeRoadTrain.getWheelCount();

Since the RoadTrain class extends a super class Car, we can also check the color:

var color = greenHugeRoadTrain.getColor();

Conclusion

I just showed a couple of small principles that I figured out at some point in the past. Using these tricks has helped me a lot. You might ask what is the purpose of using this sort of ad hoc OOP hacks to create software since you can possibly take TypeScript or something else that is a full blown OOP extension for JavaScript.

The answer is simplicity. If you can achieve a cutting edge advantage with very simple means, without adding yet another framework into your project, that will hugely reduce the overall complexity of your software. The fact is that the more dependencies you add to your project the less maintainable it will be. Just if you can create serious software with plain JavaScript can be a game changer in terms of simplicity and maintainability!

One more word of warning: If you use extensions that are no true JavaScript (such as TypeScript, CoffeeScript, GWT, just name it!) you are not creating real JavaScript code. But your compiler will turn your code into JavaScript anyway. The browser only understands JavaScript. Now, when your code needs debugging you will need to dig into plain old JavaScript anyway: you end up debugging machine-made JavaScript that is generated from your original code! So why wouldn't you code JavaScript already in the first place? Just because you can?

These examples with Car and RoadTrain are just examples, of course, to underline some concepts. They hardly have any real life value. Therefore I started writing another example of object oriented programming in JavaScript to create a neural network. Some other cases where you possibly also need inheritance could be creating different kinds of validators for text fields and other form components. All validators might have some common features like checking whether its input field contains a value or not. You might want to extend the base validator feature in all validators that inherit from it. If you want to create a validator for a number field, you might want to check two things: first, does the field have a value at all (if it is a required field)? And secondly, is the value that the user entered within an allowed range? The basic existential validation is implemented in the super class and the range validation is implemented in the subclass that extends the base class. I used inheritance in validators very recently, in a successful customer project where the imperative was to create highly dynamic forms generated in a JavaScript client running in the browser.

What's next?

It's time to play around a little with ReactJS which I'll do in my next blog post.