Chapter 6, Inheritance

Lets solve the following exercise:

Exercises

  1. Multiple inheritance by mixing into the prototype, for example:
            var my = objectMulti(obj, another_obj, a_third, { 
              additional: "properties" 
            }); 
            A possible solution: 
            function objectMulti() { 
              var Constr, i, prop, mixme; 
     
            // constructor that sets own properties 
            var Constr = function (props) { 
              for (var prop in props) { 
                this[prop] = props[prop]; 
              } 
            }; 
     
           // mix into the prototype 
           for (var i = 0; i < arguments.length - 1; i++) { 
             var mixme = arguments[i]; 
             for (var prop in mixme) { 
               Constr.prototype[prop] = mixme[prop]; 
             } 
           } 
     
          return new Constr(arguments[arguments.length - 1]);
       } 
    

    Testing:

            > var obj_a = {a: 1}; 
            > var obj_b = {a: 2, b: 2}; 
            > var obj_c = {c: 3}; 
            > var my = objectMulti(obj_a, obj_b, obj_c, {hello: "world"}); 
            > my.a; 
             2 
    

    Property a is 2 because obj_b overwrote the property with the same name from obj_a (last one wins):

            > my.b; 
            2 
            > my.c; 
            3 
            > my.hello; 
            "world" 
            > my.hasOwnProperty('a'), 
            false 
            > my.hasOwnProperty('hello'), 
            true 
    
  2. Practice with the canvas example at http://www.phpied.com/files/canvas/.

    Draw a few triangles using the following code snippet:

            new Triangle( 
              new Point(100, 155), 
              new Point(30, 50), 
              new Point(220, 00)).draw(); 
     
            new Triangle( 
              new Point(10, 15),   
              new Point(300, 50), 
              new Point(20, 400)).draw(); 
    

    Draw a few squares using the following code snippet:

            new Square(new Point(150, 150), 300).draw(); 
            new Square(new Point(222, 222), 222).draw(); 
    

    Draw a few rectangles using the following code snippet:

            new Rectangle(new Point(100, 10), 200, 400).draw(); 
            new Rectangle(new Point(400, 200), 200, 100).draw(); 
    
  3. To add Rhombus, Kite, Pentagon, Trapezoid, and Circle (reimplements draw()), use the following code:
            function Kite(center, diag_a, diag_b, height) { 
              this.points = [ 
                new Point(center.x - diag_a / 2, center.y), 
                new Point(center.x, center.y + (diag_b - height)), 
                new Point(center.x + diag_a / 2, center.y), 
                new Point(center.x, center.y - height) 
              ]; 
              this.getArea = function () { 
                return diag_a * diag_b / 2; 
              }; 
            } 
     
            function Rhombus(center, diag_a, diag_b) { 
              Kite.call(this, center, diag_a, diag_b, diag_b / 2); 
            } 
     
            function Trapezoid(p1, side_a, p2, side_b) { 
              this.points = [p1, p2, new Point(p2.x + side_b, p2.y),
              new Point(p1.x + side_a, p1.y) 
              ]; 
     
              this.getArea = function () { 
                var height = p2.y - p1.y; 
                return height * (side_a + side_b) / 2; 
              }; 
            } 
       
            // regular pentagon, all edges have the same length 
            function Pentagon(center, edge) { 
              var r = edge / (2 * Math.sin(Math.PI / 5)), 
                  x = center.x, 
                  y = center.y; 
     
              this.points = [new Point(x + r, y),
            new Point(x + r * Math.cos(2 * Math.PI / 5), y - r * 
             Math.sin(2 * Math.PI / 5)), 
            new Point(x - r * Math.cos(    Math.PI / 5), y - r * 
             Math.sin(    Math.PI / 5)), 
            new Point(x - r * Math.cos(    Math.PI / 5), y + r * 
             Math.sin(    Math.PI / 5)), 
            new Point(x + r * Math.cos(2 * Math.PI / 5), y + r * 
             Math.sin(2 * Math.PI / 5)) 
              ]; 
     
              this.getArea = function () { 
                return 1.72 * edge * edge; 
              }; 
            } 
     
            function Circle(center, radius) { 
              this.getArea = function () { 
                return Math.pow(radius, 2) * Math.PI; 
              }; 
       
              this.getPerimeter = function () { 
                return 2 * radius * Math.PI; 
              };   
       
              this.draw = function () { 
                var ctx = this.context; 
                ctx.beginPath(); 
                ctx.arc(center.x, center.y, radius, 0, 2 * Math.PI); 
                ctx.stroke(); 
              }; 
            } 
     
            (function () { 
              var s = new Shape(); 
              Kite.prototype = s; 
              Rhombus.prototype = s; 
              Trapezoid.prototype = s; 
              Pentagon.prototype = s; 
              Circle.prototype = s; 
            }()); 
    

    Testing:

            new Kite(new Point(300, 300), 200, 300, 100).draw(); 
            new Rhombus(new Point(200, 200), 350, 200).draw(); 
            new Trapezoid( 
              new Point(100, 100), 100,  
              new Point(50, 250), 400).draw(); 
            new Pentagon(new Point(400, 400), 100).draw(); 
            new Circle(new Point(500, 300), 270).draw(); 
    

    Exercises

    The result of testing new shapes

  4. Think of another way to do the inheritance part. Use uber so kids can have access to their parents. Also, get parents to be aware of their children.

    Keep in mind that not all children inherit Shape; for example, Rhombus inherits Kite and Square inherits Rectangle. You end up with something like this:

            // inherit(Child, Parent) 
            inherit(Rectangle, Shape); 
            inherit(Square, Rectangle); 
    

    In the inheritance pattern from the chapter and the previous exercise, all children were sharing the same prototype, for example:

            var s = new Shape(); 
            Kite.prototype = s; 
            Rhombus.prototype = s; 
    

    While this is convenient, it also means no one can touch the prototype because it will affect everyone else's prototype. The drawback is that all custom methods need to own properties, for example this.getArea.

    It's a good idea to have methods shared among instances and defined in the prototype, instead of recreating them for every object. The following example moves the custom getArea() methods to the prototype.

    In the inheritance function, you'll see the children only inherit the parent's prototype. So own properties such as this.lines will not be set. Therefore, you need to have each child constructor call its uber in order to get the own properties, for example:

            Child.prototype.uber.call(this, args...) 
    

    Another nice-to-have feature is carrying over the prototype properties already added to the child. This allows the child to inherit first and then add more customizations or the other way around as well, which is just a little more convenient.

            function inherit(Child, Parent) { 
              // remember prototype 
              var extensions = Child.prototype; 
     
              // inheritance with an intermediate F() 
              var F = function () {}; 
               F.prototype = Parent.prototype; 
              Child.prototype = new F(); 
              // reset constructor 
              Child.prototype.constructor = Child; 
              // remember parent 
              Child.prototype.uber = Parent; 
     
              // keep track of who inherits the Parent 
              if (!Parent.children) { 
                Parent.children = []; 
              } 
              Parent.children.push(Child); 
     
              // carry over stuff previsouly added to the prototype 
              // because the prototype is now overwritten completely 
              for (var i in extensions) { 
                if (extensions.hasOwnProperty(i)) { 
                  Child.prototype[i] = extensions[i]; 
                } 
              } 
            } 
    

    Everything about Shape(), Line(), and Point() stays the same. The changes are in the children only:

            function Triangle(a, b, c) { 
              Triangle.prototype.uber.call(this); 
              this.points = [a, b, c]; 
            } 
     
            Triangle.prototype.getArea = function () { 
              var p = this.getPerimeter(), s = p / 2; 
              return Math.sqrt(s * (s - this.lines[0].length) * 
            (s - this.lines[1].length) * (s - this.lines[2].length)); 
            }; 
     
     
            function Rectangle(p, side_a, side_b) { 
              // calling parent Shape() 
              Rectangle.prototype.uber.call(this); 
     
              this.points = [ p, 
                new Point(p.x + side_a, p.y), 
                new Point(p.x + side_a, p.y + side_b), 
                new Point(p.x, p.y + side_b) 
              ]; 
            } 
     
           Rectangle.prototype.getArea = function () { 
               // Previsouly we had access to side_a and side_b  
               // inside the constructor closure. No more. 
              // option 1: add own properties this.side_a and this.side_b 
              // option 2: use what we already have: 
              var lines = this.getLines(); 
              return lines[0].length * lines[1].length; 
            }; 
     
     
            function Square(p, side) { 
              this.uber.call(this, p, side, side); 
              // this call is shorter than Square.prototype.uber.call() 
              // but may backfire in case you inherit  
              // from Square and call uber 
              // try it :-) 
            } 
    

    Inheritance:

            inherit(Triangle, Shape); 
            inherit(Rectangle, Shape); 
            inherit(Square, Rectangle); 
    

    Testing:

            > var sq = new Square(new Point(0, 0), 100); 
            > sq.draw(); 
            > sq.getArea(); 
            10000 
    

    Testing that instanceof is correct:

            > sq.constructor === Square; 
            true 
            > sq instanceof Square; 
            true 
            > sq instanceof Rectangle; 
            true 
            > sq instanceof Shape; 
            true 
    

    The children arrays:

            > Shape.children[1] === Rectangle; 
            true 
            > Rectangle.children[0] === Triangle; 
            false 
            > Rectangle.children[0] === Square; 
            true 
            > Square.children; 
            undefined 
    

    And uber looks ok too:

            > sq.uber === Rectangle; 
            true 
    

    Calling isPrototypeOf() also returns expected results:

            Shape.prototype.isPrototypeOf(sq); 
            true 
            Rectangle.prototype.isPrototypeOf(sq); 
            true 
            Triangle.prototype.isPrototypeOf(sq); 
            false 
    

    The full code is available at http://www.phpied.com/files/canvas/index2.html, together with the additional Kite(), Circle(), and so on from the previous exercise.

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

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