One of the key features of JavaScript language is its support for prototype method. This feature could be used to bring inheritance in JavaScript.
In the beginning there was duplication
1function Person(dob) { 2 this.dob = dob; 3 this.votingAge = 21; 4}
1function Developer(dob, skills) { 2 this.dob = dob; 3 this.skills = skills || ""; 4 this.votingAge = 21; 5}
1// create a Person instance 2var person = new Person("02/02/1970"); 3 4//create a Developer instance 5var developer = new Developer("02/02/1970", "JavaScript");
As you can see both Person and Developer objects have votingAge property. This is code duplication. This is an ideal case where inheritance can be used.
prototype method
Whenever you create a function, that function instantly gets a property called prototype. The initial value of this prototype property is empty JavaScript object {} .
1var fn = function () {}; 2fn.prototype; //=> {}
JavaScript engine while looking up for a method in a function first searches for method in the function itself. Then the engine looks for that method in that functions' prototype object.
Since prototype itself is a JavaScript object, more methods could be added to this JavaScript object.
1var fn = function () {}; 2fn.prototype.author_name = "John"; 3var f = new fn(); 4f.author_name; //=> John
Refactoring code to make use of prototype method
Currently Person function is defined like this.
1function Person(dob) { 2 this.dob = dob; 3 this.votingAge = 21; 4}
Problem with above code is that every time a new instance of Person is created, two new properties are created and they take up memory. If a million objects are created then all instances will have a property called votingAge even though the value of votingAge is going to be same. All the million person instances can refer to same votingAge method if that method is define in prototype. This will save a lot of memory.
1function Person(dob) { 2 this.dob = dob; 3} 4Person.prototype.votingAge = 21;
The modified solutions will save memory if a lot of objects are created. However notice that now it will a bit longer for JavaScript engine to look for votingAge method. Previously JavaScript engine would have looked for property named votingAge inside the person object and would have found it. Now the engine will not find votingAge property inside the person object. Then engine will look for person.prototype and will search for votingAge property there. It means, in the modified code engine will find votingAge method in the second hop instead of first hop.
Bringing inheritance using prototype property
Currently Person is defined like this.
1function Person(dob) { 2 this.dob = dob; 3} 4Person.prototype.votingAge = 21;
If Developer Object wants to extend Person then all that needs to be done is this.
1function Developer(dob, skills) { 2 this.skills = skills || ""; 3 this.dob = dob; 4} 5Developer.prototype = new Person();
Now Developer instance will have access to votingAge method. This is much better. Now there is no code duplication between Developer and Person.
However notice that looking for votingAge method from a Developer instance will take an extra hop.
- JavaScript engine will first look for votingAge property in the Developer instance object.
- Next engine will look for votingAge property in its prototype property of Developer instance which is an instance of Person. votingAge method is not declared in the Person instance.
- Next engine will look for votingAge property in the prototype of Person instance and this method would be found.
Since only the methods that are common to both Developer and Person are present in the Person.prototype there is nothing to be gained by looking for methods in the Person instance. Next implementation will be removing the middle man.
Remove the middle man
Here is the revised implementation of Developer function.
1function Developer(dob, skills) { 2 this.skills = skills || ""; 3 this.dob = dob; 4} 5Developer.prototype = Person.prototype;
In the above case Developer.prototype directly refers to Person.prototype. This will reduce the number of hops needed to get to method votingAge by one compared to previous case.
However there is a problem. If Developer changes the common property then instances of person will see the change. Here is an example.
1Developer.prototype.votingAge = 18; 2var developer = new Developer("02/02/1970", "JavaScript"); 3developer.votingAge; //=> 18 4 5var person = new Person(); 6person.votingAge; //=> 18. Notice that votingAge for Person has changed from 21 to 18
In order to solve this problem Developer.prototype should point to an empty object. And that empty object should refer to Person.prototype .
Solving the problem by adding an empty object
Here is revised implementation for Developer object.
1function Developer(dob, skills) { 2 this.dob = dob; 3 this.skills = skills; 4} 5var F = function () {}; 6F.prototype = Person.prototype; 7Developer.prototype = new F();
Let's test this code.
1Developer.prototype.votingAge = 18; 2var developer = new Developer("02/02/1970", "JavaScript"); 3developer.votingAge; //=> 18 4 5var person = new Person(); 6person.votingAge; //=> 21
As you can see with the introduction of empty object, Developer instance have votingAge of 18 while Person instance have votingAge of 21.
Accessing super
If child wants to access super object then that should be allowed. That can be accomplished like this.
1function Person(dob) { 2 this.dob = dob; 3} 4Person.prototype.votingAge = 21; 5 6function Developer(dob, skills) { 7 this.dob = dob; 8 this.skills = skills; 9} 10var F = function () {}; 11F.prototype = Person.prototype; 12Developer.prototype = new F(); 13Developer.prototype.__super = Person.prototype; 14Developer.prototype.votingAge = 18;
Capturing it as a pattern
The whole thing can be captured in a helper method that would make it simple to create inheritance.
1var extend = function (parent, child) { 2 var F = function () {}; 3 F.prototype = parent.prototype; 4 child.prototype = new F(); 5 child.prototype.__super = parent.prototype; 6};
Pure prototypal inheritance
A simpler form of pure prototypal inheritance can be structured like this.
1if (typeof Object.create !== "function") { 2 Object.create = function (o) { 3 function F() {} 4 F.prototype = o; 5 return new F(); 6 }; 7}
Before adding the create method to object, I checked if this method already exists or not. That is important because Object.create is part of ECMAScript 5 and slowly more and more browsers will start adding that method natively to JavaScript.
You can see that Object.create takes only one parameter. This method does not necessarily create a parent child relationship . But it can be a very good tool in converting an object literal to a function.