为什么我不使用 Child.prototype = Parent.Prototype 而不是 Child.prototype = new Parent(); 用于 Javascript 继承?

IT技术 javascript inheritance
2021-02-09 05:53:05

我不明白 javascript 中的这种继承行为我一直看到它是这样定义的:

function GameObject(oImg, x, y) {

    this.x = x;
    this.y = y;
    this.img = oImg;

    this.hit = new Object();
    this.hitBox.x = x;
    this.hitBox.y = y;
    this.hitBox.width = oImg.width;
    this.hitBox.height = oImg.height;

}

Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;

function Spaceship(){
    console.log("instantiate ship");
    GameObject.apply(this, arguments);
    this.vx = 0;
    this.vy = 0;
    this.speed = 3;
    this.friction = 0.94;
}

但就我而言,这些行:

    this.hitBox.width = oImg.width;
    this.hitBox.height = oImg.height;

当我在我的 Spaceship 构造函数中执行 console.log(this) 时,我可以看到proto属性设置为 Spaceship 而不是 GameObject,如果我删除它们,它会设置为 GameObject。

如果我使用:

 Spaceship.prototype = GameObject.prototype;

我没有更多的问题。这阻止我的原因是我有另一个带有 add() 方法的对象,它使用以下代码检查对象是否符合 GameObject :

 if(object instanceof GameObject)

我不明白这两行可能会改变什么,以便在它们存在时继承被破坏,我不确定以第二种方式进行继承是否好。有人可以启发我吗?:)

3个回答

如果你这样做

Spaceship.prototype = GameObject.prototype;

那么它们都引用同一个对象,所以你最好把所有东西都放在 中GameObject,如果你添加一些东西到Spaceship.prototype,它也会被添加到GameObject.prototype您可以通过Spaceship.prototype在分配后添加一些内容来轻松测试它例如,在您的情况下,您可以看到GameObject.prototype.constructor实际上是Spaceship.

至于

Spaceship.prototype = new GameObject();

这会调用可能具有不良副作用的构造函数,您宁愿使用:

Spaceship.prototype = Object.create(GameObject.prototype);

这里使用的Object.create功能归结为:

Object.create = function( proto ) {
    function f(){}
    f.prototype = proto;
    return new f;
};

不过现代浏览器已经具备了这个功能。

非常感谢这解决了我的问题,我现在对继承有了更好的理解!:)
2021-03-16 05:53:05
是的,我刚发表评论就看到了,我做了很多改变^^
2021-03-24 05:53:05
@GeoffreyHug 你有没有注意到你没有.hitBox在任何地方定义this.hit = new Object(); this.hitBox.x = x;应该是this.hitBox = new Object();
2021-03-25 05:53:05
你应该提到,当使用它时Object.create(),你通常需要从子构造函数调用父构造函数。
2021-03-27 05:53:05

从来没有正确解释过为什么你会出现奇怪的行为this.hitBox(我认为这就是你想说的)。

如果通过调用父类的构造函数来创建原型来进行继承,则该父类的构造函数执行一次以创建父类型的实例,然后子类型的所有实例将共享该实例作为其原型。

问题在于,如果该构造函数有任何this行将可变对象分配给,那么这些对象将是该原型上的属性,并且对这些对象的任何修改都将反映在子类型的所有实例中:

Spaceship.prototype = new GameObject();
Spaceship.prototype.constructor = Spaceship;

var sps1 = new Spaceship();
var sps2 = new Spaceship();

sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x);  // 12   (oh noes! what happened)
console.log(sps2.hitBox.x);  // 12

(“调用构造函数来制作原型”方法还有其他类似的问题,但我将把它留在这里)

@Esilija 的使用建议Object.create(baseObject)是解决这个问题的第一步。它创建了一个新对象,其原型为baseObject,但没有在构造函数中设置的内容(这是一件好事,但需要考虑。继续阅读...)。

正如我刚才所说,这将创建一个对象,其中父构造函数中的初始化逻辑从未运行过,但在大多数情况下,该逻辑与对象的功能相关。因此,您还需要做一件事,即让子构造函数调用父构造函数:

function Spaceship(oImg, x, y) {
    // call parent constructor on this object and pass in arguments.
    // you could also use default values for the arguments when applicable
    GameObject.call(this, oImg, x, y);

    // remainder of Spaceship constructor...
}

这将确保父构造函数逻辑为每个 new单独运行Spaceship,并执行必要的初始化任务。

function GameObject(oImg, x, y) {

    this.x = x;
    this.y = y;
    this.img = oImg || {width:null, height: null};

    this.hitBox = new Object();
    this.hitBox.x = x;
    this.hitBox.y = y;
    this.hitBox.width = this.img.width;
    this.hitBox.height = this.img.height;

}


function Spaceship(){
    GameObject.apply(this, arguments);
    this.vx = 0;
    this.vy = 0;
    this.speed = 3;
    this.friction = 0.94;
}
Spaceship.prototype = new GameObject();

var sps1 = new Spaceship();
var sps2 = new Spaceship();

sps1.hitBox.x = 9;
sps2.hitBox.x = 12;
console.log(sps1.hitBox.x);  // 9
console.log(sps2.hitBox.x);  // 12