Javascript“OOP"和具有多级继承的原型 [英] Javascript "OOP" and prototypes with multiple-level inheritance

查看:29
本文介绍了Javascript“OOP"和具有多级继承的原型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Javascript 编程的新手,我正在从面向对象编程的角度处理我的第一个应用程序(实际上是一个游戏)(我知道 js 并不是真正的面向对象,但对于这个特定问题,它对我来说更容易就这样开始)

I'm new to Javascript programming and I'm approaching my first application (a game, indeed) from an object oriented programming perspective (I know js is not really object oriented, but for this particular problem it was easier for me to start like this).

我有一个类"层次结构,其中最顶层(事物"类)定义了相关事物(游戏中的附加项目)的列表.它是由 ThingA1 和 ThingA2 类继承的 ThingA 类继承的.

I have a hierarchy of "classes" where the top-most ("Thing" class) defines a list of related things (attached items in the game). It's inherited by a ThingA class which is inherited by ThingA1 and ThingA2 classes.

最小的例子是:

function Thing() 
{
  this.relatedThings   = [];
}
Thing.prototype.relateThing = function(what)
{
  this.relatedThings.push(what);
}

ThingA.prototype = new Thing();
ThingA.prototype.constructor = ThingA;
function ThingA()
{
}

ThingA1.prototype = new ThingA();
ThingA1.prototype.constructor = ThingA1;
function ThingA1()
{

}

ThingA2.prototype = new ThingA();
ThingA2.prototype.constructor = ThingA2;
function ThingA2()
{    
}

var thingList = [];

thingList.push(new ThingA());
thingList.push(new ThingA1());
thingList.push(new ThingA2());
thingList.push(new ThingA2());
thingList.push(new Thing());

thingList[1].relateThing('hello');

在代码的最后,当执行相关事物时,每一个 ThingA、ThingA1 和 ThingA2 都将执行它(不是数组中的最后一个事物"对象).我发现如果我在 ThingA 原型中定义了 relatedThing 函数,它会正常工作.由于游戏的设计方式,我宁愿不必这样做.

At the end of the code, when the relateThing is executed, every ThingA, ThingA1 and ThingA2 is going to execute it (not the last "Thing" object in the array). I've found if I define the relateThing function in the ThingA prototype, it will work right. Because of how the game is designed I'll prefer not to have to do that.

也许我不了解原型在 javascript 中的工作原理.我知道该功能在所有对象之间共享,但我想执行将是单独的.有人可以解释为什么会发生这种情况以及如何解决吗?我不知道我是否在继承、原型定义或其他方面做错了.

Maybe I'm not understanding something about how the prototypes work in javascript. I know the function is shared among all the objects, but i guess the execution would be individual. Could somebody explain why is this happening and how to solve it? I don't know if I'm doing the inheritance wrong, or the prototypes definitions, or what.

提前致谢.

推荐答案

欢迎来到原型链!

让我们看看它在您的示例中的样子.

Let's see what it looks like in your example.

当您调用 new Thing() 时,您正在创建一个具有属性 relatedThings 的新对象,该属性引用一个数组.所以我们可以说我们有这个:

When you call new Thing(), you are creating a new object with a property relatedThings which refers to an array. So we can say we have this:

+--------------+
|Thing instance|
|              |
| relatedThings|----> Array
+--------------+     

然后您将此实例分配给 ThingA.prototype:

You are then assigning this instance to ThingA.prototype:

+--------------+
|    ThingA    |      +--------------+
|              |      |Thing instance|
|   prototype  |----> |              |
+--------------+      | relatedThings|----> Array
                      +--------------+

所以 ThingA 的每个实例都将从 Thing 实例继承.现在您将创建 ThingA1ThingA2 并为它们的每个原型分配一个新的 ThingA 实例,然后创建 的实例ThingA1ThingA2(以及 ThingAThing,但此处未显示).

So each instance of ThingA will inherit from the Thing instance. Now you are going to create ThingA1 and ThingA2 and assign a new ThingA instance to each of their prototypes, and later create instances of ThingA1 and ThingA2 (and ThingA and Thing, but not shown here).

现在的关系是这样的(__proto__ 是一个内部属性,将一个对象与其原型连接起来):

The relationship is now this (__proto__ is an internal property, connecting an object with its prototype):

                               +-------------+
                               |   ThingA    |
                               |             |    
+-------------+                |  prototype  |----+
|   ThingA1   |                +-------------+    |
|             |                                   |
|  prototype  |---> +--------------+              |
+-------------+     |    ThingA    |              |
                    | instance (1) |              |
                    |              |              |
+-------------+     |  __proto__   |--------------+ 
|   ThingA1   |     +--------------+              |
|   instance  |           ^                       |
|             |           |                       v
|  __proto__  |-----------+                 +--------------+
+-------------+                             |Thing instance|
                                            |              |
                                            | relatedThings|---> Array
+-------------+     +--------------+        +--------------+ 
|   ThingA2   |     |   ThingA     |              ^
|             |     | instance (2) |              |
|  prototype  |---> |              |              |
+-------------+     |  __proto__   |--------------+
                    +--------------+
+-------------+           ^
|   ThingA2   |           |  
|   instance  |           |
|             |           |
|  __proto__  |-----------+
+-------------+                        

正因为如此,ThingAThingA1ThingA2 的每一个实例都指向同一个数组实例.

And because of that, every instance of ThingA, ThingA1 or ThingA2 refers to one and the same array instance.

不是你想要的!

为了解决这个问题,任何子类"的每个实例都应该有自己的 relatedThings 属性.您可以通过在每个子构造函数中调用父构造函数来实现这一点,类似于在其他语言中调用 super():

To solve this problem, each instance of any "subclass" should have its own relatedThings property. You can achieve this by calling the parent constructor in each child constructor, similar to calling super() in other languages:

function ThingA() {
    Thing.call(this);
}

function ThingA1() {
    ThingA.call(this);
}

// ...

这会调用 ThingThingA 并将这些函数内的 this 设置为您传递给 .call.详细了解 .call [MDN]this [MDN].

This calls Thing and ThingA and sets this inside those function to the first argument you pass to .call. Learn more about .call [MDN] and this [MDN].

仅此一项就会将上图改为:

This alone will change the above picture to:

                               +-------------+
                               |   ThingA    |
                               |             |    
+-------------+                |  prototype  |----+
|   ThingA1   |                +-------------+    |
|             |                                   |
|  prototype  |---> +--------------+              |
+-------------+     |    ThingA    |              |
                    | instance (1) |              |
                    |              |              |
                    | relatedThings|---> Array    |
+-------------+     |  __proto__   |--------------+ 
|   ThingA1   |     +--------------+              |
|   instance  |           ^                       |
|             |           |                       |
|relatedThings|---> Array |                       v
|  __proto__  |-----------+                 +--------------+
+-------------+                             |Thing instance|
                                            |              |
                                            | relatedThings|---> Array
+-------------+     +--------------+        +--------------+ 
|   ThingA2   |     |   ThingA     |              ^
|             |     | instance (2) |              |
|  prototype  |---> |              |              |
+-------------+     | relatedThings|---> Array    |
                    |  __proto__   |--------------+
                    +--------------+
+-------------+           ^
|   ThingA2   |           |  
|   instance  |           |
|             |           |
|relatedThings|---> Array | 
|  __proto__  |-----------+
+-------------+

如您所见,每个实例都有自己的 relatedThings 属性,它指向不同的数组实例.原型链中仍然有relatedThings属性,但它们都被实例属性遮蔽.

As you can see, each instance has its own relatedThings property, which refers to a different array instance. There are still relatedThings properties in the prototype chain, but they are all shadowed by the instance property.

另外,不要设置原型:

ThingA.prototype = new Thing();

您实际上不想在此处创建新的 Thing 实例.如果 Thing 期望参数会发生什么?你会通过哪一个?如果调用 Thing 构造函数有副作用怎么办?

You actually don't want to create a new Thing instance here. What would happen if Thing expected arguments? Which one would you pass? What if calling the Thing constructor has side effects?

实际上想要的是将Thing.prototype 连接到原型链中.您可以使用 Object.create 来做到这一点[MDN]:

What you actually want is to hook up Thing.prototype into the prototype chain. You can do this with Object.create [MDN]:

ThingA.prototype = Object.create(Thing.prototype);

在构造函数 (Thing) 执行时发生的任何事情都会稍后发生,当我们实际创建一个新的 ThingA 实例时(通过调用 Thing.call(这) 如上所示).

Anything that happens when the constructor (Thing) is executed will happen later, when we actually create a new ThingA instance (by calling Thing.call(this) as shown above).

这篇关于Javascript“OOP"和具有多级继承的原型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆