javascript中的构造函数方法影响对象的所有实例

javascript中的构造函数方法影响对象的所有实例,javascript,object,methods,constructor,instance,Javascript,Object,Methods,Constructor,Instance,我正在开发一个俄罗斯方块的实现来自学javascript,我遇到了一个问题。首先是背景:我为每种类型的俄罗斯方块都有一个类,它继承自一个基本的方块模板,使用移动和返回坐标等方法。比如说, function BlockTemplate(w, h, c){ var height = h; var width = w; var x = 0; var y = 0; var color = c; this.getHeight = function(){re

我正在开发一个俄罗斯方块的实现来自学javascript,我遇到了一个问题。首先是背景:我为每种类型的俄罗斯方块都有一个类,它继承自一个基本的方块模板,使用移动和返回坐标等方法。比如说,

function BlockTemplate(w, h, c){
    var height = h;
    var width = w;
    var x = 0;
    var y = 0;
    var color = c;

    this.getHeight = function(){return height;};
    this.getWidth = function(){return width;};
    this.getX = function(){return x;};
    this.getY = function(){return y;};
    this.getColor = function(){return color;};
    this.moveTo = function(newX, newY){
        x = newX;
        y = newY;
    };
    this.translate = function(dx, dy){
        x += dx;
        y += dy;
    };
}

function StraightBlock(){
    this.draw = function(){
        ctx.fillStyle = this.getColor();
        ctx.fillRect(this.getX(), this.getY(), 20, 100);
    };
}
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");
当前屏幕上的所有块都存储在一个数组中,
blockArr
,但当前下降的块除外,该块存储在
curBlock

我使用一个名为
createRandomBlock()
的函数创建一个块并将其放入路缘锁:

var blockTypeArr = [LBlock, SquareBlock, StraightBlock, SquigBlock];

var createRandomBlock = function(){
    var block = blockTypeArr[Math.floor(Math.random()*blockTypeArr.length)];
    var randomBlock = new block();
    return randomBlock;
};

curBlock = createRandomBlock();
落下后,我将其放入阵列并创建一个新块:

blockArr[blockArr.length] = curBlock;
curBlock = createRandomBlock();
如果新创建的块尚未出现在屏幕上,则没有问题。但是,使用新实例的
moveTo
translate
方法会影响该类的所有实例(我添加了一个id属性以确保它们实际上是不同的实例)

例如,使用JavaScript控制台

>curBlock
  SquigBlock
>blockArr
  [SquareBlock, SquigBlock, SquigBlock]
如您所见,到目前为止,已经有3个爆破块落下(2个在
blockArr
中,1个正在落下)。然而,我看到的唯一一个是当前掉落的(
curBlock
),并且检查参数,
curBlock.getY()
blockArr[1]。getY()
blockArr[2]。getY()
都返回相同的值。换句话说,它们都在同一个位置绘制

如何更改它,使旧块(无论是什么类)保持在屏幕底部,而新创建的块从顶部落下,而不会导致该类的所有其他块随之移动

谢谢大家!

好吧,这只是一个与
StraightBlock
实例共享的
BlockTemplate
。您可以看到
new StraightBlock().moveTo==new StraightBlock().moveTo
,即两个实例具有非常相同的方法,该方法会影响相同的
x
变量,但是:

由于只调用一次,因此有一个
var x
由单个
getHeight
关闭,该
getHeight
在所有
直线块的实例中共享

您应该进行两项更改:

1使用存储在此
上的实例字段
2个链式构造器
让
StraightBlock
调用它的超类构造函数,向创建的每个实例添加新版本的方法

function StraightBlock(){
  BlockTemplate.apply(this, arguments);
  ...
}

您也可以只进行构造函数链接,但这意味着您正在为每个实例创建每个方法的新副本。如果您有很多“类”的短期实例,这可能会很昂贵


在此
上存储字段的缺点是,原始代码可能会无意中对字段进行变异。JavaScript没有强大的信息隐藏功能,除了通过封闭局部变量,因此如果您希望通过其他语言中的
private
字段获得健壮性,那么您当前通过局部变量进行的设计可能是最好的。

第一次更改几乎没有必要,并且使getter方法变得多余。@Bergi,如果您使用的是闭包编译器之类的东西,那么getter和setter是积极内联的,您可以获得某种程度的保护,以防止意外的变异。啊,我现在看到我的逻辑失败的地方了。BlockTemplate.apply(这个,args)是否与StraightBlock.prototype=Object.create(BlockTemplate.prototype)做相同的事情?或者有区别吗?
对象。创建(…)
创建一个没有属性的空白对象<代码>块模板。应用(此,args)
使用通过调用
新建StraightBlock
创建的对象,并运行
块模板的主体来设置其属性。可能存在重复的Brilliant!Object.create只是创建块模板原型的副本吗?没错,如果通过“复制”,您关注的是它没有在那里调用构造函数这一事实。但是,它不会“复制”属性,而是从BlockTemplate原型继承。
function StraightBlock(){
    BlockTemplate.call(this, 20, 100, "#00FFE5");
    this.draw = function(){
        ctx.fillStyle = this.getColor();
        ctx.fillRect(this.getX(), this.getY(), 20, 100);
    };
}
StraightBlock.prototype = Object.create(BlockTemplate.prototype);
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");
function BlockTemplate(w, h, c){
   this.height = h;
   ...
}

BlockTemplate.prototype.getHeight = function(){return this.height;};
...
function StraightBlock(){
  BlockTemplate.apply(this, arguments);
  ...
}