2013-12-09 114 views
3

我花了大約12個小時看這個代碼,並擺弄它,試圖找出哪裏有遞歸問題,因爲我得到了「最大調用堆棧大小超過「,錯誤,並沒有找到它。有人比我聰明,請幫助我!超過最大調用堆棧大小 - 沒有明顯的遞歸

到目前爲止,我發現的是,當我將對象,spot,一個circle,對象,問題消失,但是當我使它成爲'點',我得到這個堆棧溢出錯誤。我用friggin'顯微鏡去了pip課,但仍然不知道爲什麼會發生這種情況!

var canvas = document.getElementById('myCanvas'); 

//------------------------------------------------------------------------------------- 
// Classes 
//------------------------------------------------------------------------------------- 
//===================================================================================== 
//CLASS - point 
function point(x,y){ 
    this.x = x; 
    this.y = y; 
} 
//===================================================================================== 
// CLASS - drawableItem 
function drawableItem() { 
    var size = 0; 
    this.center = new point(0,0); 
    this.lineWidth = 1; 
    this.dependentDrawableItems = new Array(); 
} 
//returns the size 
drawableItem.prototype.getSize = function getSize(){ 
    return this.size; 
} 
// changes the size of this item and the relative size of all dependents 
drawableItem.prototype.changeSize = function(newSize){ 
    var relativeItemSizes = new Array; 
    relativeItemSizes.length = this.dependentDrawableItems.length; 
    // get the relative size of all dependent items 
    for (var i = 0; i < this.dependentDrawableItems.length; i++){ 
     relativeItemSizes[i] = this.dependentDrawableItems[i].getSize()/this.size; 
    } 
    // change the size 
    this.size = newSize; 
    // apply the ratio of change back to all dependent items 
    for (var i = 0; i < relativeItemSizes.length; i++){ 
     this.dependentDrawableItems[i].changeSize(relativeItemSizes[i] * newSize); 
    } 
} 
//moves all the vertices and every dependent to an absolute point based on center 
drawableItem.prototype.moveTo = function(moveX,moveY){ 
    //record relative coordinates 
    var relativeItems = new Array; 
    relativeItems.length = this.dependentDrawableItems.length; 
    for (var i = 0; i < relativeItems.length; i++){ 
     relativeItems[i] = new point; 
     relativeItems[i].x = this.dependentDrawableItems[i].center.x - this.center.x; 
     relativeItems[i].y = this.dependentDrawableItems[i].center.y - this.center.y; 
    } 
    //move the center 
    this.center.x = moveX; 
    this.center.y = moveY; 
    //move all the items relative to the center 
    for (var i = 0; i < relativeItems.length; i++){ 
     this.dependentDrawableItems[i].moveItemTo(this.center.x + relativeItems[i].x, 
      this.center.y + relativeItems[i].y); 
    } 
} 
// draws every object in dependentDrawableItems 
drawableItem.prototype.draw = function(ctx){ 
    for (var i = 0; i < this.dependentDrawableItems.length; i++) { 
     this.dependentDrawableItems[i].draw(ctx); 
    } 
} 

//===================================================================================== 
//CLASS - circle 
function circle(isFilledCircle){ 
    drawableItem.call(this); 
    this.isFilled = isFilledCircle 
} 
circle.prototype = new drawableItem(); 
circle.prototype.parent = drawableItem.prototype; 
circle.prototype.constructor = circle; 
circle.prototype.draw = function(ctx){ 
    ctx.moveTo(this.center.x,this.center.y); 
    ctx.beginPath(); 
    ctx.arc(this.center.x, this.center.y, this.size, 0, 2*Math.PI); 
    ctx.closePath(); 
    ctx.lineWidth = this.lineWidth; 
    ctx.strokeStyle = this.outlineColor; 
    if (this.isFilled === true){ 
     ctx.fill(); 
    }else { 
     ctx.stroke(); 
    } 
    this.parent.draw.call(this,ctx); 
} 

//===================================================================================== 
//CLASS - pip 
function pip(size){ 
    circle.call(this,true); 
} 
pip.prototype = new circle(false); 
pip.prototype.parent = circle.prototype; 
pip.prototype.constructor = pip; 

//---------------------------------------------------------------------- 
// Objects/variables - top layer is last (except drawable area is first) 
//---------------------------------------------------------------------- 
var drawableArea = new drawableItem(); 

var spot = new pip(); 
spot.changeSize(20); 
drawableArea.dependentDrawableItems[drawableArea.dependentDrawableItems.length] = spot; 

//------------------------------------------ 
// Draw loop 
//------------------------------------------ 
function drawScreen() { 
    var context = canvas.getContext('2d'); 
    context.canvas.width = window.innerWidth; 
    context.canvas.height = window.innerHeight; 

    spot.moveTo(context.canvas.width/2, context.canvas.height/2); 

    drawableArea.draw(context); 
} 

window.addEventListener('resize', drawScreen); 

這裏的演示:http://jsfiddle.net/DSU8w/

+0

還不夠好的答案,但我有一個偷偷摸摸的懷疑你有一些非構造函數試圖作爲構造函數。 Google閉包拋出了一些關於'Property getSize never defined'的東西,一些函數不是構造函數!看看http://stackoverflow.com/a/5991265/588079和http://stackoverflow.com/a/4613017/588079和http://stackoverflow.com/q/6095530/588079。看看是否有幫助,否則試着削減代碼的一部分,直到你得到這個溢出的最小值,然後把它分享到一個小提琴中。 – GitaarLAB

+0

***警告***(對於同行專家):這段代碼可能會導致瀏覽器崩潰(我的兩次崩潰),所以如果你有重要的東西打開/不打開(例如其他jsfiddle),請不要隨意擺弄它。 @jgrant:儘量不要讓小提琴自動運行,而是在按下按鈕之類的簡單用戶操作之後,這樣人們就有機會真正地加載小提琴並在發生崩潰之前檢查源代碼。 – GitaarLAB

+0

我想「drawableItem.prototype.changeSize」導致你的問題。 –

回答

5
this.parent.draw.call(this,ctx); 

是你的問題。在pip對象上,父項將爲circle.prototype。所以,當你現在稱之爲spot.draw(),它會調用spot.parent.draw.call(spot),其中this.parent仍然是circle.prototype ...

您將需要從circle.prototype.draw明確調用drawableItem.prototype.draw.call(this)。順便說一句,你應該not use new for the prototype chain

+0

我開始研究爲什麼不使用'新'和替代品......現在我的大腦正在融化。 – jgrant

0

你爲什麼要這樣寫代碼?這很難理解和調試。當我創建很多類時,我通常使用augment來構建我的代碼。這是我怎麼會重寫代碼:

var Point = Object.augment(function() { 
    this.constructor = function (x, y) { 
     this.x = x; 
     this.y = y; 
    }; 
}); 

使用augment,你可以清晰地創建類。

var DrawableItem = Object.augment(function() { 
    this.constructor = function() { 
     this.size = 0; 
     this.lineWidth = 1; 
     this.dependencies = []; 
     this.center = new Point(0, 0); 
    }; 

    this.changeSize = function (toSize) { 
     var fromSize = this.size; 
     var ratio = toSize/fromSize; 
     this.size = toSize; 

     var dependencies = this.dependencies; 
     var length = dependencies.length; 
     var index = 0; 

     while (index < length) { 
      var dependency = dependencies[index++]; 
      dependency.changeSize(dependency.size * ratio); 
     } 
    }; 

    this.moveTo = function (x, y) { 
     var center = this.center; 
     var dx = x - center.x; 
     var dy = y - center.y; 
     center.x = x; 
     center.y = y; 

     var dependencies = this.dependencies; 
     var length = dependencies.length; 
     var index = 0; 

     while (index < length) { 
      var dependency = dependencies[index++]; 
      var center = dependency.center; 

      dependency.moveTo(center.x + dx, center.y + dy); 
     } 
    }; 

    this.draw = function (context) { 
     var dependencies = this.dependencies; 
     var length = dependencies.length; 
     var index = 0; 

     while (index < length) dependencies[index++].draw(context); 
    }; 
}); 

傳承也很簡單:舉例如下您drawableItem類可以進行重組。例如,你可以調整你的circlepip類如下:

var Circle = DrawableItem.augment(function (base) { 
    this.constructor = function (filled) { 
     base.constructor.call(this); 
     this.filled = filled; 
    }; 

    this.draw = function (context) { 
     var center = this.center; 
     var x = center.x; 
     var y = center.y; 

     context.moveTo(x, y); 

     context.beginPath(); 
     context.arc(x, y, this.size, 0, 2 * Math.PI); 
     context.closePath(); 

     context.lineWidth = this.lineWidth; 
     context[this.filled ? "fill" : "stroke"](); 
     base.draw.call(this, context); 
    }; 
}); 

var Pip = Circle.augment(function (base) { 
    this.constructor = function() { 
     base.constructor.call(this, true); 
    }; 
}); 

現在你已經創建了所有的類,你終於可以踏踏實實地繪圖:

window.addEventListener("DOMContentLoaded", function() { 
    var canvas = document.getElementById("myCanvas"); 
    var context = canvas.getContext("2d"); 
    var drawableArea = new DrawableItem; 
    var spot = new Pip; 

    spot.changeSize(20); 
    drawableArea.dependencies.push(spot); 
    window.addEventListener("resize", drawScreen, false); 

    drawScreen(); 

    function drawScreen() { 
     var width = canvas.width = window.innerWidth; 
     var height = canvas.height = window.innerHeight; 
     spot.moveTo(width/2, height/2); 
     drawableArea.draw(context); 
    } 
}, false); 

我們就大功告成了。請參閱演示文稿:http://jsfiddle.net/b5vNk/

我們不僅讓您的代碼更具可讀性,易於理解和維護,而且我們還解決了遞歸問題。

由於Bergi提到的問題是在circle.prototype.draw函數中聲明this.parent.draw.call(this,ctx)。由於spot.parentcircle.prototypethis.parent.draw.call(this,ctx)聲明等同於circle.prototype.draw.call(this,ctx)。正如你所看到的,circle.prototype.draw現在遞歸地調用它自己,直到它超過最大遞歸深度並拋出一個錯誤。

augment庫優雅地解決了這個問題。

var DerivedClass = BaseClass.augment(function (base) { 
    console.log(base === BaseClass.prototype); // true 
}); 

base參數應被視爲一個常數,而不必在每個原型創建parent屬性,當你增加一類augment爲您提供該類作爲一個參數的prototype(我們稱之爲base)的。因爲這是Circle類中的一個常量base.draw.call(this, context)將始終等於DrawableItem.prototype.draw.call(this, context)。因此,你永遠不會有不必要的遞歸。與this.parent不同,base論點總是指向正確的原型。

0

BERGI的答案是正確的,如果你不想硬編碼父名多次,你可以使用一個輔助函數來設置繼承:

function inherits(Child,Parent){ 
    Child.prototype=Object.create(Parent.prototype); 
    Child.parent=Parent.prototype; 
    Child.prototype.constructor=Child; 
}; 
function DrawableItem() { 
    this.name="DrawableItem"; 
} 
DrawableItem.prototype.changeSize = function(newSize){ 
    console.log("changeSize from DrawableItem"); 
    console.log("invoking object is:",this.name); 
} 
function Circle(isFilledCircle){ 
    Circle.parent.constructor.call(this); 
    this.name="Circle";//override name 
} 
inherits(Circle,DrawableItem); 
Circle.prototype.changeSize = function(newSize){ 
    Circle.parent.changeSize.call(this); 
    console.log("and some more from circle"); 
}; 
function Pip(size){ 
    Pip.parent.constructor.call(this,true); 
    this.name="Pip"; 
} 
inherits(Pip,Circle); 

var spot = new Pip(); 
spot.changeSize(); 

有關的Object.create一個填充工具看here

相關問題