手记

JavaScript面向对象、继承小案例:图形绘制

首先将所有对象的公共部分定义成一个构造器,即Shape。然后基于这个构造器分别构建Triangle、Rectangle和Square构造器,它们将全部继承于Shape。其中Square实际上可以被当作一个长宽相等的Rectangle,因此当构建Square时可以直接重用Rectangle。

Shape体系中共有属性主要包括:
     一个能根据给定的point绘制出图形的draw()方法;
     一个getParimeter()方法;
     一个用于存储point对象的数组属性;
     其他必须的属性与方法;

关于绘制部分用到<canvas>标签。

<canvas width="600" height="800" id="canvas"></canvas>

需要两个辅助构造器,Point和Line。其中,Point用于定义图形,而Line则用于计算给定点之间的距离。

首先是定义辅助构造器Point

function Point (x, y) {
        this.x = x;
        this.y = y;
    }

构造器Line,根据勾股定理计算出给定两点之间的直线距离

  function Line (p1, p2) {
        this.p1 = p1;
        this.p2 = p2;
        this.length = Math.sqrt(
            Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)
        );
    }

Shape构造器的定义

   function Shape () {
        this.points = [];
        this.lines = [];
        this.init();
    }

定义Shape.prototype的方法。用对象标识法来定义所有的方法

 Shape.prototype = {
        constructor: Shape,//重置constructor属性
        init: function () {
            if (this.context === undefined) {
                var canvas = document.getElementById('canvas');
                Shape.prototype.context = canvas.getContext('2d');
            }
        },
        //遍历所有点,画出一个形状
        draw: function () {
            var ctx = this.context;
            ctx.strokeStyle = this.getColor();
            ctx.beginPath();
            ctx.moveTo(this.points[0].x, this.points[0].y);
            for (var i = 1; i < this.points.length; i++) {
                ctx.lineTo(this.points[i].x, this.points[i].y);
            }
            ctx.closePath();
            ctx.stroke();
        },
        //随机生成颜色
        getColor: function () {
            var rgb = [];
            for (var i = 0; i < 3; i++) {
                rgb[i] = Math.round(255 * Math.random());
            }
            return 'rgb(' + rgb.join() + ')';
        },
        //遍历所有点,生成线条实例,并添加进this.lines
        getLines: function () {
            if (this.lines.length) {
                return this.lines;
            }
            var lines = [];
            for (var i = 0; i < this.points.length; i++) {
                lines[i] = new Line(this.points[i], this.points[i + 1] || this.points[0]);
            }
            this.lines = lines;
            return lines;
        },
        //由子对象实现
        getArea: function () {},
        //计算所有线条的长度
        getPerimeter: function () {
            var perim = 0, lines = this.getLines();
            for (var i = 0; i < lines.length; i++) {
                perim += lines[i].length;
            }
            return perim;
        }
    };

接着是自对象构造器,从Triangle开始:

function Triangle(a, b, c) {
        this.points = [a, b, c];
        this.getArea = function () {
            var p = this.getPerimeter();
            var s = p / 2;
            return Math.sqrt(
                s * (s - this.lines[0].length)  * (s - this.lines[1].length) 
                * (s - this.lines[2].length)
            );
        };
    }

Rectangle构造器,它接收的参数是一个point对象(即左上角位置)和两边的长度。

function Rectangle (p, side_a, side_b) {
        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)
        ];
        this.getArea = function () {
            return side_a * side_b;
        };
    }

最后一个子对象构造器是Square。由于Square是Rectangle的一种特例,所以对于它的实现,可以重用Rectangle,而其中最简单的莫过于构造器借用法了;

  function Square (p, side) {
        Rectangle.call(this, p, side, side);
    }

所有构造器的实现都已经完成。开始处理它们之间的继承关系,几乎所有的仿传统模式(即工作方式是基于构造器而非对象的模式)都符合这里的需求。下面试着将其修改为原型链模式,并提供一个简化版本。该模式中,需要新建一个父对象实体,然后直接将其设置为自对象的原型。这样一来,就没有必要为每个子对象的原型创建新的实体了-----因为它们可以通过原型实现完全共享。

 (function () {
        var s = new Shape();
        Triangle.prototype = s;
        Rectangle.prototype = s;
        Square.prototype = s;
    })();

测试:

//定义Triangle的三个point
    var p1 = new Point(100, 100);
    var p2 = new Point(300, 100);
    var p3 = new Point(200, 0);

    //将这三个point传递给Triangle构造器,以创建一个Triangle实例
    var t = new Triangle(p1, p2, p3);
    t.draw();
    console.log(t.getPerimeter());  //输出482.842712474619
    console.log(t.getArea());   //输出10000.000000000002

    //Rectangle的实例化
    var r = new Rectangle(new Point(200, 200), 50, 100);
    r.draw();
    console.log(r.getArea());   //输出5000
    console.log(r.getPerimeter());  //输出300

    //Square的实例化
    var s = new Square(new Point(130, 130), 50);
    s.draw();
    console.log(s.getArea());   //输出2500
    console.log(s.getPerimeter());  //输出200

    //重用triangle的point
    new Square(p1, 200).draw();

最终测试结果下图所示:

1人推荐
随时随地看视频
慕课网APP