欢迎来到原型链!
让我们看看它在您的示例中是什么样子的。
问题
当您调用 时new Thing()
,您正在创建一个具有relatedThings
引用数组的属性的新对象。所以我们可以说我们有这个:
+--------------+
|Thing instance|
| |
| relatedThings|----> Array
+--------------+
然后,您将此实例分配给ThingA.prototype
:
+--------------+
| ThingA | +--------------+
| | |Thing instance|
| prototype |----> | |
+--------------+ | relatedThings|----> Array
+--------------+
所以每个实例ThingA
都会继承自该Thing
实例。现在您将创建ThingA1
并为它们的每个原型ThingA2
分配一个新ThingA
实例,然后创建 and 的实例ThingA1
(ThingA2
和ThingA
and Thing
,但此处未显示)。
现在的关系是这样的(__proto__
是一个内部属性,将一个对象与其原型连接起来):
+-------------+
| 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__ |-----------+
+-------------+
正因为如此,ThingA
, ThingA1
or的每个实例都ThingA2
指向同一个数组实例。
这不是你想要的!
解决方案
为了解决这个问题,任何“子类”的每个实例都应该有自己的 relatedThings
属性。您可以通过在每个子构造函数中调用父构造函数来实现这一点,类似于super()
在其他语言中调用:
function ThingA() {
Thing.call(this);
}
function ThingA1() {
ThingA.call(this);
}
// ...
这会在这些函数内部调用Thing
andThingA
并将其设置this
为您传递给的第一个参数.call
。了解有关.call
[MDN]和this
[MDN]的更多信息。
仅此一项就会将上图更改为:
+-------------+
| 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
属性,但它们都被实例属性所遮蔽。
更好的继承
另外,不要将原型设置为:
ThingA.prototype = new Thing();
您实际上不想在Thing
这里创建新实例。如果Thing
预期的参数会发生什么?你会通过哪一个?如果调用Thing
构造函数有副作用怎么办?
你真正想要的是连接Thing.prototype
到原型链中。您可以使用Object.create
[MDN]执行此操作:
ThingA.prototype = Object.create(Thing.prototype);
执行构造函数 ( ) 时发生的任何事情都会在Thing
稍后发生,当我们实际创建一个新ThingA
实例时(通过Thing.call(this)
如上所示的调用)。