Inheriting the prototype only

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:

  • Not creating a new object for the sake of inheritance alone
  • Having fewer lookups during runtime (when it comes to searching for 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 temporary constructor - new F()

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.79.70