Javascript继承案例学习:绘制图形
这是一个关于继承的应用示例。示例的任务是计算各种不同图形的面积和边界,然后将它们绘制出来。并且,要求在这过程中尽可能地实现代码重用。
分析
首先,我们要将所有对象的公共部分定义成一个构造器,即Shape。然后我们基于这个构造器分别构建我们的Triangle、Rectangle 和Square 构造器,它们将全部继承于Shape。
Shape 体系中的共有属性主要包括:
- 一个能根据给定的point 绘制出图形的draw()方法。
- 一个getParameter()方法。
- 一个用于存储point 对象的数组属性。
- 其他必须的属性与方法。
关于绘制部分,我们还将用到标签。还有两个辅助构造器不能不提—Point 和Line。其中,Point 用于定义图形,而Line 则用于计算给定两个点之间的距离。
实现
1 首先,我们要在空白的HTML 页面中添加一个canvas 标签;然后将JavaScript 代码放到script标签里:
<canvas height="600" width="800" id="canvas" />
<script>
// ... code goes here
</script>
2 构造器定义
function Point(x, y) {
this.x = x;
this.y = y;
}
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)
);
}
function Shape() {
this.points = [];
this.lines = [];
this.init();
}
3 定义Shape.Prototype 的方法。下面我们用对象标识法来定义所有的方法。
Shape.prototype = {
// 重置坐标
constructor: Shape,
// 初始化,如果有canvas对象,设置坐标的上下文
init: function() {
if (this.context === undefined) {
var canvas = document.getElementById('canvas');
Shape.prototype.context = canvas.getContext('2d');
}
},
// 通过坐标绘制图形
draw: function () {
var i, ctx = this.context;
ctx.strokeStyle = this.getColor();
ctx.beginPath();
ctx.moveTo(this.points[0].x, this.points[0].y);
for (i = 1; i<this.points.length; i++) {
ctx.lineTo(this.points[i].x, this.points[i].y);
}
ctx.closePath();
ctx.stroke();
},
// 生成随机的颜色
getColor: function () {
var i, rgb = [];
for (i = 0; i< 3; i++) {
rgb[i] = Math.round(255 * Math.random());
}
return 'rgb(' + rgb.join(',') + ')';
},
// 通过坐标数组,创建线的实例,并添加到shape的lines属性中
getLines: function() {
if (this.lines.length > 0) {
return this.lines;
}
var i, lines = [];
for (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 i, perim = 0, lines = this.getLines();
for (i = 0; i<lines.length; i++) {
perim += lines[i].length;
}
return perim;
}
}
// 子对象构建器:三角形
function Triangle(a, b, c){
this.points = [a, b, c];
this.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){
this.points = [
p,
new Point(p.x + side_a, p.y), // top right
new Point(p.x + side_a, p.y + side_b), // bottom right
new Point(p.x, p.y + side_b) // bottom left
];
this.getArea = function() {
return side_a * side_b;
};
}
// 子对象构建器:正方形。由于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;
})();
// 测试
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);
var t = new Triangle(p1, p2, p3);
t.draw();
t.getPerimeter();
t.getArea();
var r = new Rectangle(new Point(200, 200), 50, 100);
r.draw();
r.getPerimeter();
r.getArea();
var s = new Square(new Point(130, 130), 50);
s.draw();
s.getPerimeter();
s.getArea();
new Square(p1, 200).draw();