2017-02-10 109 views
1

我是一位Ruby开发人员,他最终决定认真学习JavaScript。所以我购买了一些书,然后开始潜入,但当我试图了解原型继承时,我很快就陷入了困境......JavaScript原型继承和HTML画布

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

  • 当我调用Triangle和Rectangle实例的绘图函数时,该方法将正确绘制它们。
  • 当我添加第二种方法来显示他们的名字时,每个实例都会正确记录它。

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

这里是运动的代码:

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函数填充矩形,尽管我在三角形实例上调用它?当人们正确回答翻转两个绘制函数调用时,我会更好地指定以下内容。问题不在于形状的颜色,而在于上下文指针。为什么只有最后一个形状被填满?如果在调用fill之前添加更多形状,则只有最后一个填充。这意味着我在画布上做错了事。我认为这是“我绘制形状的地方”,但它似乎更像“最后一个活动形状”
  • 如何修复该代码以使其正常工作填充想要的形状只要我想要?我的意思是。如果我想要一个接收特定形状的实例并填充它的函数呢?
  • 有没有办法访问包含在画布中的绘图?
+0

在你的'make_me_crazy'功能,切换您的绘制调用(第一绘制矩形,然后三角形)。结果应该告诉你一些关于上下文的东西! – chazsolo

+0

是的,你是对的。我认为背景是“我正在研究的形状”,但不是......是帆布? 如果我添加第三个形状,fill方法填充它。所以我推断上下文是最后一个活动形状,无论哪个人调用'fill'方法..但是为什么?在任何时候如何正确引用形状? –

回答

0

关于您的问题的要点。

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

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

当你实例RectangleTriangle,他们都设置name。 另一方面,render方法仅适用于Shape原型。

关于第二点(和第三个): 也许你在Triangle上绘制Rectangle。尝试切换draw调用的顺序来检查它。

+0

渲染方法? mmhh也许这就是重点,因为我对所有形状使用相同的画布,也许我应该将上下文重置为我想填充的形状......但是如何? –

+0

而且,不,这两个形状不重叠。 (如果我添加其他形状,填充方法总是填充最后一个) –

1

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

注意在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>

+0

也是。我让我重新思考我的问题,以找到更好的方式来表达我的疑问。谢谢! –

+0

这是正确的答案,我正在删除我的。 –