Object Oriented JavaScript - Inheritance


Up until now, we've talked about Object Oriented JavaScript programming. Creating classes though doesn't make you code truly object oriented. What you lack is polymorphism, which is achieved through inheritance.

JavaScript is a bit confusing for developers coming from Java or C++, as it's all dynamic, all runtime, and it has no classes at all. It's all just instances (objects). Even the "classes" we simulate are just a function object.

Prototype Chain


When it comes to inheritance, JavaScript only has one construct: objects. Each object has an internal link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. null, by definition, has no prototype, and acts as the final link in this prototype chain.

Object.create


After being long advocated forEcmaScript 5 has standardized a new method called Object.create. To keep up with progress, I'll be presenting inheritance using only this method. For those who want to use it with incompatible browsers, you must add its polyfill.

As the name states, the method creates a new object with the specified prototype object and properties. To understand the meaning of this, let's take a look at example:
function Mammal() {
  this.age = 0;
  console.info("Mammal was born");
}

Mammal.prototype.grow = function() {
    this.age += 1;
    console.log("Mammal grows");
};

function Dog(name) {
  this.name = name;
  Mammal.call(this);
}

Dog.prototype = Object.create(Mammal.prototype);
Dog.prototype.constructor = Dog;
var m = new Mammal(), d = new Dog('Rocky');
m.grow();
d.grow();
At first we created our superclass, Mammal, with attribute age and method grow. After that we declared class Dog, with its constructor and additional attribute, name. The inheritance happens in line 16, where our creation method takes Mammal's prototype and creates a new object inherited from it. Assigning it to Dog's prototype attribute, closes the loop. Take a look at line 11, where Dog's constructor is declared. Inside we call our Mammal constructor, by using call method. Together with constructor substitution in line 17, we achieve a proper relationship of our constructors. Otherwise instances of Dog would have a constructor of Mammal and attribute name wouldn't be initiated. The output of the code, can be seen below:
Mammal was born
Mammal was born
Mammal grows
Mammal grows 
You can see that Mammal's constructor as well as it's method is called when using both Mammal and Dog instances. Now what if we wanted to override the grow method with our own? The following code does exactly this:
Dog.prototype.grow=function(){ 
 Mammal.prototype.grow.call(this);
 this.age =+ 1; 
 console.log('Dog grows');
}
Calling the previous sequence again will produce the wanted results. Now we can see that our new implementation of Dog.grow is called in addition or Mammal's one.
Mammal was born
Mammal was born
Mammal grows
Mammal grows
Dog grows 
Rather than having to know that Dog inherits from Mammal, and having to type in Mammal.prototype each time you wanted to call an ancestor method, wouldn't it be nice to have your own property of the Dog pointing to its ancestor class? Those familiar with other Object Oriented languages may be tempted to call this property super, however JavaScript reserves this word for future use. Instead, we'll call it _super. The code of course produces the same results, however in my opinion it's somehow cleaner.
Dog.prototype._super = Mammal.prototype;

Dog.prototype.grow=function(){ 
 this._super.grow.call(this);
 this.age =+ 1; 
 console.log('Dog grows');
}
Object.create can take additional parameter, properties, which aid us to define new properties following Object.defineProperty syntax to the newly created class. In our example we could add new properties to the Dog this way:
Dog.prototype = Object.create(Mammal.prototype {
 color: { writable: true,  configurable:true, value: 'brown' }
});
I find it distasteful as it breaks your class definition from your methods. It surely makes it more tedious to define your attributes. However if you like it and find it useful, you may use it as well.

Object.create vs new


Object.create is not a new operator and they are not fully interchangeable as some suggest and shouldn't be considered as such. First of all with Object.create you can create an object that doesn't inherit from anything, by passing null as a prototype parameter - Object.create(null). Setting prototype attribute with null, and instantiate a class using new operator, will create a class inherited from Object.prototype. Secondly the performance of Object.create is dreadful and should be only used for class definitions, leaving the creation process to the operator. The measurements results can be seen here.

What about multiple inheritance?


To answer in one sentence - you should avoid it. Take a look at any wide spread modern object oriented language like Java, Ruby, C# and PHP (5 of course :) - non of them allows multiple inheritance. It brings more problems than benefits and most of the times your need of multiple inheritance is a signal your object structure is somewhat incorrect. Follow the SOLID principles and you will be able to solve everything without needing to resort to such measures.

This is not only my unprofessional opinion looking for a way to shine, but also a point of view of one of the most notable man in the sphere - Bjarne Stroustrup.  Take a look at excerpt from an interview over C++ modern style:
People quite correctly say that you don't need multiple inheritance, because anything you can do with multiple inheritance you can also do with single inheritance. You just use the delegation trick I mentioned. Furthermore, you don't need any inheritance at all, because anything you do with single inheritance you can also do without inheritance by forwarding through a class. Actually, you don't need any classes either, because you can do it all with pointers and data structures. But why would you want to do that? When is it convenient to use the language facilities? When would you prefer a workaround? I've seen cases where multiple inheritance is useful, and I've even seen cases where quite complicated multiple inheritance is useful. Generally, I prefer to use the facilities offered by the language to doing workarounds.
But if you insist, it can be accomplished through inheritance by copying properties, also known as mixinsjQuery.extend is the most commonly used implementation.

Comments

Popular posts from this blog

Rust Static Analysis and Blockchain Licensing

Length extension attack

CAP Theorem and blockchain