当子对象具有从其原型对象继承的属性时,真正发生的是子对象具有参考到包含属性的原型。孩子没有自己的副本。所以两个孩子都使用同一个数组——(一)上的一个Parent
您分配给的原型对象Child.prototype
.
首先是一些图片,然后是更多文字。 :-)
new Parent()
给你这个:
+-----------------+
| Parent instance |
+-----------------+
| list = [] |
+-----------------+
...然后您分配给Child.prototype
.
Then, new Child()
给你这个:
+------------------+
| Child instance 1 |
+------------------+ +-----------------+
| (prototype) |------->| Parent instance |
+------------------+ +-----------------+
| list = [] |
+-----------------+
Doing new Child()
再次给你另一个:
+------------------+ +------------------+
| Child instance 1 | | Child instance 2 |
+------------------+ +------------------+
| (prototype) |---+ | (prototype) |---+
+------------------+ | +------------------+ |
| |
| | +-----------------+
+-------------------------+---->| Parent instance |
+-----------------+
| list = [] |
+-----------------+
正如您所看到的,所有Child
实例是sharing相同Parent
实例(他们的原型),上面有一个数组。
当你说:
var obj = new Child();
obj.list.push("foo");
...这是 JavaScript 引擎在看到信息时会执行的操作obj.list
:
- 看看有没有
obj
有它的own属性称为list
。如果没有,那么:
- 看看有没有
obj
的原型有其own属性称为list
。如果没有,那么:
- 看看有没有
obj
的原型 的原型有其own属性称为list
.
- 等等,直到我们用完原型。
就你而言,自从obj
没有它的own list
属性,引擎着眼于它的原型(Parent
您分配给的实例Child.prototype
),在本例中确实具有该属性。所以就用了那个。它不会复制给孩子或任何东西,它是used。当然,由于它是一个数组,因此将某些内容推送到数组上实际上是将其推送到数组上。
如果你要assign某事obj.list
, then obj
会得到它的own list
财产,打破链条。所以说this.list = [];
in Child
会给每个孩子自己的清单。
每当原型有一个时你就会看到这一点对象引用其上,对象是可以修改的类型(“可变”对象)。数组、日期、普通对象 ({}
)、正则表达式等,它们都有状态并且都可以修改,所以您会看到它们。 (String
实例是不可变的,因此尽管发生了同样的事情,但您看不到任何效果,因为字符串无法更改。)
对于原语,虽然你继承了它们,但你不能改变它们的状态(你只能replace它们具有不同状态的新原语),因此您不会看到相同的效果。因此,如果obj
继承财产foo
从它的原型来看,foo
is 42
, alert(obj.foo)
将从原型中获取值并显示“42”。改变的唯一方法foo
就是说obj.foo = 67
或类似的——这给出了obj
its own备份foo
,与原型的副本不同。 (即使您使用类似的东西也是如此++
and --
, e.g. ++obj.foo
;这确实被评估为obj.foo = obj.foo + 1
).