Javascript Classes, Inheritance and Mixins - Deep explanation

Javascript Classes, Inheritance and Mixins - Deep explanation

In JavaScript you make the rules, you say how you want your classes to work or not to work. Now I will show you my way to create classes in JavaScript;

Basic class

var MyClass = function(argument1, argument2){
    this.__argument1 = argument1;
    this.__argument2 = argument2;
}


// static/class method
MyClass.classMethod = function(){};


_.extend(MyClass.prototype, {

    instanceMethod: function(){}

})


// instance and use like this
var instance = new MyClass(1, 2);
instance.instanceMethod();
MyClass.classMethod();

As you can see we are making use of the the _ symbol which is the underscore library. The èxtend method is used in order to extend the prototype of the class. This is required if you don't want to repeat the words MyClass.prototype for every method. You also cannot do like this:

// DON'T DO THIS!
MyClass.protoype = {

    instanceMethod: function(){};

}

because you will mess up the class and the constructor will not be called.

Inheritance

var MySubClass = function(){
    // call the super-class constructor
    MyClass.prototype.constructor.apply(this, arguments);
}


_.extend(MySubClass.protoype, SubClass.prototype, {

    instanceMethod: function(){
        // call the super-class method "instanceMethod"
        MyClass.prototype.instanceMethod.apply(this, arguments);
    },

    anotherInstanceMethod: function(){}

})

Mixins

As I've told you before in JavaScript you make the rules, so if you like to use mixins then you can do it!
A mixin is a set of named methods that are dirrectly attached to your class. You can apply multiple mixins in a class.

var MyMixin1 = {
    mixinMethod1: function(){}
}
var MyMixin2 = {
    mixinMethod2: function(){}
}

Now if you would want to include those mixins in the MySubClass you just have to make a small change:

_.extend(
    MySubClass.protoype, 
    SubClass.prototype, 
    MyMixin1, 
    MyMixin2, {

    instanceMethod: function(){
        // call the super-class method "instanceMethod"
        MyClass.prototype.instanceMethod.apply(this, arguments);

    },

    anotherInstanceMethod: function(){}
})

Now you may asking what happens when a mixin will override an already defined method. Well that's your task to manage what happens. By changing the order of the arguments of the extend method you change the override settings. Remember that the righter argument overrides the previous (left one).

Another way to implement mixins is to check if the method is already defined and throw an error in this case like in the Actor Class.

Conventions

A good practice in programming is to be consistent and one way to do it is by setting conventions. Every class begins with a capital letter like this MyClass and every instance begins with a small letter like this myClass. Also the methods starts with a small letter like this instanceMethod, this is both valid for instance and class methods.
In JavaScript there are no private and protected methods by default, therefore I like to enforce the following convention:

  • Private methods begin with 2 underscores like this __myPrivateMethod;
  • Protected methods begin 1 underscore like this _myProtectedMethod;

Passing arguments to the constructor

Another convention I use is to pass an object as the argument of the class. This has 2 immediate benefits:

  • When you instance a class, you already know what you are passing as arguments, as objects are named; Check this example new Car({wheels: 4, weightKG: 1500}) and compare to new Car(4, 1500); You write less but you hurt the readability. You should always write to be read;
  • You can use defaults for your arguments cleanly;

Check the example below:

var MyClass = function(o){
    // defaults
    o = _.extend({
        wheels  : 2,
        weightKG: 1000,
    }, o)
    this.__wheels   = o.wheels;
    this.__weightKG = o.wheightKG;
}

Memory leaks?

By attaching the methods to the prototype directly this will not lead to any memory leak.
Anyway you must be aware that the methods of the parent Class are always copied to the child's class and this will increase slightly the memory but will reduce the time to look-up for the method because it doesn't have to check in each parent prototype for the method. Se above for a deep explanation.

How a method is find in an instance?

When you call the method this.myMethod() (or any attribute) the following things will happen in our process:

  • Find any attribute in the current object (instance) named myMethod; if found run it;
  • Find any attribute in the prototype of the class named myMethod; if found run it;

So there are only 2 look-ups for each method called on this; That's a performance boost;

Now if you do prototype inheritance by doing this:

MySubClass.prototype = new MyClass()
MySubClass.prototype.instanceMethod = function(){};

Then you will create a prototype chain and the look up for the methods called from MySubClass looks like this:

  • Find any attribute in the current object (instance) named myMethod; if found run it;
  • Find any attribute in the prototype of the class named myMethod; if found run it;
  • Find any attribute in the prototype's prototype of the class named myMethod; if found run it;

If you have multiple parents this look-up will take more time as it has to check for each parent's prototype.

Further tweaks

You can build more stuff on classes but I prefer to keep them minimal and don't make complicated processes for instancing classes or building them.
For example some more complex way of using mixins is by using the method described in the Actor Class and Javascript plugin (controlled mixin).

Subscribe to receive new interesting posts about programming

comments powered by Disqus