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
Object.create
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 growsYou 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 growsRather 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?
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 mixins. jQuery.extend is the most commonly used implementation.