【问题标题】:Constructor methods in javascript affect all instances of objectjavascript中的构造方法影响对象的所有实例
【发布时间】:2014-02-22 02:36:25
【问题描述】:

我正在实现俄罗斯方块来自学 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()的函数来创建一个块并将其放入curBlock:

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();

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

例如,使用 JavaScript 控制台,

>curBlock
  SquigBlock
>blockArr
  [SquareBlock, SquigBlock, SquigBlock]

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

我怎样才能改变它,使旧块,无论是什么类,都留在屏幕底部,而新创建的块从顶部落下,而不导致该类的所有其他块随之移动?

谢谢!

【问题讨论】:

标签: javascript object methods constructor instance


【解决方案1】:
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");

由于这被调用了一次,因此有一个 var x 被单个 getHeight 封闭,该 getHeight 在所有 StraightBlock 实例之间共享。

你应该做两处改变:

1 使用存储在this 上的实例字段

function BlockTemplate(w, h, c){
   this.height = h;
   ...
}

BlockTemplate.prototype.getHeight = function(){return this.height;};
...

2 链构造函数

StraightBlock 调用它的超类构造函数,为每个创建的实例添加新版本的方法。

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

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

this 上存储字段的缺点是它们可能会被天真的代码无意中改变。 JavaScript 除了通过封闭的局部变量外没有强大的信息隐藏功能,因此如果您想要通过其他语言中的 private 字段获得的健壮性,那么您当前通过局部变量进行的设计可能是最好的。

【讨论】:

  • 第一次更改几乎没有必要,并且使 getter 方法变得多余。
  • @Bergi,如果您使用像 Closure Compiler 这样的东西,那么 getter 和 setter 会积极内联,并且您会获得一定程度的保护,防止意外突变。
  • 啊,我现在知道我的逻辑在哪里失败了。 BlockTemplate.apply(this, args) 是否与 StraightBlock.prototype = Object.create(BlockTemplate.prototype) 做同样的事情?还是有区别?
  • Object.create(...) 创建一个没有属性的空白对象。 BlockTemplate.apply(this, args) 使用调用new StraightBlock 创建的对象并运行BlockTemplate 的主体来设置其属性。
【解决方案2】:
StraightBlock.prototype = new BlockTemplate(20, 100, "#00FFE5");

嗯,只有一个BlockTemplateStraightBlock 实例共享。您可以看到new StraightBlock().moveTo == new StraightBlock().moveTo,即两个实例具有完全相同的方法,这确实会影响相同的x 变量。 Do not use new for creating the prototype,但是Correct javascript inheritance

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);

【讨论】:

  • 太棒了! Object.create 是否只是创建 BlockTemplate 原型的副本?
  • 确切地说,如果通过“复制”,您关注的事实是它不会在那里调用构造函数。但是,它不会“复制”属性,而是从 BlockTemplate 原型继承的 creates an empty object
猜你喜欢
  • 1970-01-01
  • 2023-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 1970-01-01
  • 1970-01-01
  • 2021-02-28
相关资源
最近更新 更多