超过了最大调用堆栈大小-没有明显的递归
maximum call stack size exceeded - no apparent recursion
我花了大约12个小时浏览这段代码,并对其进行了处理,试图找出哪里存在递归问题,因为我收到了"超过了最大调用堆栈大小"的错误,但没有找到。请比我聪明的人帮我!
到目前为止,我只发现当我将对象spot
设为circle
对象时,问题消失了,但当我将其设为"pip"时,我会出现堆栈溢出错误。我已经用该死的显微镜复习了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/
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)
。顺便说一句,原型链不应该使用new
。
为什么要写这样的代码?它很难理解和调试。当我创建很多类时,我通常使用augment
来构建代码。这就是我将如何重写你的代码:
var Point = Object.augment(function () {
this.constructor = function (x, y) {
this.x = x;
this.y = y;
};
});
使用augment
可以干净地创建类。例如,您的drawableItem
类可以按如下方式进行重组:
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);
};
});
继承也很简单。例如,您可以按如下方式重组circle
和pip
类:
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.parent
是circle.prototype
,因此this.parent.draw.call(this,ctx)
语句等效于circle.prototype.draw.call(this,ctx)
。正如您所看到的,circle.prototype.draw
函数现在递归地调用自己,直到它超过最大递归深度并抛出错误。
augment
库优雅地解决了这个问题。当您扩充类时,不必在每个原型上创建parent
属性。augment
为您提供了该类的prototype
作为参数(我们称之为base
):
var DerivedClass = BaseClass.augment(function (base) {
console.log(base === BaseClass.prototype); // true
});
base
参数应被视为常量。因为它是一个常数,所以上面Circle
类中的base.draw.call(this, context)
总是等价于DrawableItem.prototype.draw.call(this, context)
。因此,您永远不会有不需要的递归。与this.parent
不同,base
参数总是指向正确的原型。
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上的polyfill,请查看此处。
- 超过了最大调用堆栈大小.递归标签
- Javascript递归函数引用了这一点
- 超过了最大调用堆栈大小-没有明显的递归
- 递归过程中的Returning(1)完成了什么
- 尽管我努力了,递归时钟调用仍在滴答作响
- Chrome 未捕获范围错误:最大调用堆栈大小超出了点击递归
- 为什么递归单词搜索求解器缺少五个单词,却找到了其他 47 个单词
- (非常简单的递归)我错过了什么
- Javascript - 递归 - 迭代 json,用户能够跳过
- 为什么我达到了 $digest () 迭代?如何跟踪递归脚本的活动
- JavaScript递归:超过了最大调用堆栈大小
- javascript递归函数:Uncaught RangeError:超过了最大调用堆栈大小
- 是什么导致了“;无方法”;函数递归调用自身时出错
- 如何显示在递归函数中添加了哪些变量
- 我得到了错误:太多的递归.为什么呢?
- JavaScript中的递归太多了
- 遍历网格的递归函数发疯了
- 使用ng-include和ng-repeat的递归在第一级之后就不起作用了
- jQuery:递归太多了,但我需要为扫雷舰递归
- 在数组中将数字相加,没有任何循环.我使用递归解决了.有没有更好的方法