As explained earlier, for reasons of efficiency, you should add the reusable properties and methods to the prototype. If you do so, then it's a good idea to inherit only the prototype, because all the reusable code is there. This means that inheriting the Shape.prototype
object is better than inheriting the object created with new Shape()
. After all, new Shape()
only gives you own shape properties that are not meant to be reused (otherwise, they would be in the prototype). You gain a little more efficiency by:
toString()
)For example, here's the updated code; the changes are highlighted:
function Shape() {} // augment prototype Shape.prototype.name = 'Shape'; Shape.prototype.toString = function () { return this.name; }; function TwoDShape() {} // take care of inheritance TwoDShape.prototype = Shape.prototype; TwoDShape.prototype.constructor = TwoDShape; // augment prototype TwoDShape.prototype.name = '2D shape'; function Triangle(side, height) { this.side = side; this.height = height; } // take care of inheritance Triangle.prototype = TwoDShape.prototype; Triangle.prototype.constructor = Triangle; // augment prototype Triangle.prototype.name = 'Triangle'; Triangle.prototype.getArea = function () { return this.side * this.height / 2; };
The test code gives you the same result:
>var my = new Triangle(5, 10); >my.getArea(); 25 >my.toString(); "Triangle"
What's the difference in the lookups when calling my.toString()
? First, as usual, the JavaScript engine looks for a toString()
method of the my
object itself. The engine doesn't find such a method, so it inspects the prototype. The prototype turns out to be pointing to the same object that the prototype of TwoDShape
points to and also the same object that Shape.prototype
points to. Remember that objects are not copied by value, but only by reference. So, the lookup is only a two-step process as opposed to four (in the previous example) or three (in the first example).
Simply copying the prototype is more efficient, but it has a side effect because, all the prototypes of the children and parents point to the same object, when a child modifies the prototype, the parents get the changes and so do the siblings.
Look at the following line:
Triangle.prototype.name = 'Triangle';
It changes the name
property, so it effectively changes Shape.prototype.name
too. If you create an instance using new Shape()
, its name
property says "Triangle"
:
>var s = new Shape(); >s.name; "Triangle"
This method is more efficient, but may not suit all your use cases.
A solution to the previously outlined problem, where all prototypes point to the same object and the parents get children's properties, is to use an intermediary to break the chain. The intermediary is in the form of a temporary constructor function. Creating an empty function F()
and setting its prototype
to the prototype of the parent constructor allows you to call new F()
and create objects that have no properties of their own, but inherit everything from the parent's prototype
.
Let's take a look at the modified code:
function Shape() {} // augment prototype Shape.prototype.name = 'Shape'; Shape.prototype.toString = function () { return this.name; }; function TwoDShape() {} // take care of inheritance var F = function () {}; F.prototype = Shape.prototype; TwoDShape.prototype = new F(); TwoDShape.prototype.constructor = TwoDShape; // augment prototype TwoDShape.prototype.name = '2D shape'; function Triangle(side, height) { this.side = side; this.height = height; } // take care of inheritance var F = function () {}; F.prototype = TwoDShape.prototype; Triangle.prototype = new F(); Triangle.prototype.constructor = Triangle; // augment prototype Triangle.prototype.name = 'Triangle'; Triangle.prototype.getArea = function () { return this.side * this.height / 2; };
Creating my
triangle and testing the methods:
>var my = new Triangle(5, 10); >my.getArea(); 25 >my.toString(); "Triangle"
Using this approach, the prototype chain stays in place:
>my.__proto__ === Triangle.prototype; true >my.__proto__.constructor === Triangle; true >my.__proto__.__proto__ === TwoDShape.prototype; true >my.__proto__.__proto__.__proto__.constructor === Shape; true
Also, the parents' properties are not overwritten by the children:
>var s = new Shape(); >s.name; "Shape" >"I am a " + new TwoDShape(); // calling toString() "I am a 2D shape"
At the same time, this approach supports the idea that only properties and methods added to the prototype should be inherited and own properties should not. The rationale behind this is that own properties are likely to be too specific to be reusable.
3.133.79.70