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();
最终测试结果下图所示:
共同学习,写下你的评论
评论加载中...
作者其他优质文章