2017-02-10 71 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

這是正確的答案,我正在刪除我的。 –