JavaScript原型继承和html画布



我是一名Ruby开发人员,最终决定认真学习JavaScript。所以我买了一些书,开始深入研究,但当我试图理解原型继承时,我很快就陷入了困境。。。

这本书的一个例子如下。给定一个原型有绘制方法的Shape,以及两个子形状:一个三角形和一个矩形,原型继承自Shape;

  • 当我在三角形和矩形实例上调用draw函数时,该方法将正确绘制它们
  • 当我添加第二个方法来显示它们的名称时,每个实例都会正确地记录它

一切都可以完全理解,直到我添加了第三种方法来填充形状。。。只有最后一个被填满了。不管我打哪个电话。为什么?画布上有什么特别的东西吗?

这是练习的代码:

function Point(x, y) {
this.x = x;
this.y = y;
}
function Shape() {
this.points = [];
this.init();
}
Shape.prototype = {
constructor: Shape,
init: function() {
if (this.context === undefined) {
Shape.prototype.context = document.getElementById('canvas').getContext('2d');
};
if (this.name === undefined) {
Shape.prototype.name = 'generic shape'
}
},
draw: function() {
var i, ctx = this.context;
ctx.strokeStyle = 'rgb(0,0,255)';
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();
},
fill: function(color) {
var ctx = this.context;
ctx.fillStyle = color;
ctx.fill();
},
say_name: function() {
console.log('Hello my name is ' + this.name)
}
};
function Triangle(a, b, c) {
this.points = [a, b, c];
this.name = 'Triangle'
this.context = document.getElementById('canvas').getContext('2d');
}
function Rectangle(side_a, side_b) {
var p = new Point(200, 200);
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.name = 'Rectangle'
this.context = document.getElementById('canvas').getContext('2d');
}
(function() {
var s = new Shape();
Triangle.prototype = s;
Rectangle.prototype = s;
})();
function testTriangle() {
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);
return new Triangle(p1, p2, p3);
}
function testRectangle() {
return new Rectangle(100, 100);
}
function make_me_crazy() {
var t = testTriangle();
var r = testRectangle();
t.draw();
r.draw();
t.say_name();
r.say_name();
t.fill('red');
}
make_me_crazy();
<canvas height='600' width='800' id='canvas' />

谢谢!

更多详细信息:

  • 为什么函数say_name正是我想要说的:"我是三角形"或"我是矩形",而不是"我是通用形状",但fill函数填充了矩形,尽管我在三角形实例上调用它?当人们正确地回答翻转两个draw函数调用时,我会更好地指定以下内容。问题不在于形状的颜色,而在于上下文指针。为什么只填充最后一个形状?如果在调用fill之前添加更多形状,则只有最后一个形状被填充。这意味着我在画布上做错了什么。我以为这是"我画形状的地方",但它似乎更像是"最后一个活跃的形状">
  • 我如何修复该代码,使其在任何时候都能正确填充我想要的形状?我是说。如果我想要一个函数来接收特定形状的实例并填充它,该怎么办
  • 有什么方法可以访问画布中包含的绘图吗

问题的核心是上下文-您的形状共享画布的单个上下文,因此在对象之间来回翻转不是直接的。相反,把你的操作顺序想象成一次处理一个形状,只有在处理完前一个形状后才转到下一个形状。

注意make_me_crazy函数中的调用顺序:

function Point(x, y) {
this.x = x;
this.y = y;
}
function Shape() {
this.points = [];
this.init();
}
Shape.prototype = {
constructor: Shape,
init: function(){
if (this.context === undefined) {
Shape.prototype.context = document.getElementById('canvas').getContext('2d');
};
if(this.name === undefined){
Shape.prototype.name = 'generic shape'
}
},
draw: function(){
var i, ctx = this.context;
ctx.strokeStyle = 'rgb(0,0,255)';
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();
},
fill: function(color){
var ctx = this.context;
ctx.fillStyle = color;
ctx.fill();
},
say_name: function(){console.log('Hello my name is '+ this.name)}
};
function Triangle(a,b,c){
this.points = [a, b, c];
this.name = 'Triangle'
this.context = document.getElementById('canvas').getContext('2d');
}
function Rectangle(side_a, side_b){
var p = new Point(200, 200);
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.name = 'Rectangle'
this.context = document.getElementById('canvas').getContext('2d');
}
(function(){
var s = new Shape();
Triangle.prototype = s;
Rectangle.prototype = s;
})();
function testTriangle(){
var p1 = new Point(100, 100);
var p2 = new Point(300, 100);
var p3 = new Point(200, 0);
return new Triangle(p1, p2, p3);
}
function testRectangle(){
return new Rectangle(100, 100);
}
function make_me_crazy(){
var t = testTriangle();
t.say_name();
t.draw();
t.fill('red');

var r = testRectangle();
r.draw();
r.say_name();
}
make_me_crazy();
<canvas height='600' width='800' id='canvas'></canvas>

关于问题的要点。

对于第一个:关键是这行代码

if(this.name === undefined){
Shape.prototype.name = 'generic shape'
}

当实例化RectangleTriangle时,它们都会设置name。另一方面,render方法仅在Shape原型中可用。

关于第二点(以及第三点):也许你在把Rectangle画在Triangle上。尝试切换draw调用的顺序以进行检查。