JavaScript继承问题不一致(所有子对象的父对象的单个实例…是否?)

JavaScript继承问题不一致(所有子对象的父对象的单个实例…是否?),javascript,inheritance,Javascript,Inheritance,为了为我正在从事的一个项目建立一个健全的、可维护的继承模型,我对JavaScript进行了越来越深入的研究。我遇到了一些奇妙的资源,指出了一个有趣的问题,即将一个“类”的原型指向另一个“类”的新实例。它用来说明这一点 具体来说,本文指出,通过以下代码(从上面链接的第二篇文章中提取)创建“Cat”实例将导致每个“Cat”实例指向同一个“哺乳动物”实例 正如指出这一点的文章(上面链接的第一篇)所示,使用此代码会产生以下结果: var myPet = new Cat('Felix'); myPet.h

为了为我正在从事的一个项目建立一个健全的、可维护的继承模型,我对JavaScript进行了越来越深入的研究。我遇到了一些奇妙的资源,指出了一个有趣的问题,即将一个“类”的原型指向另一个“类”的新实例。它用来说明这一点

具体来说,本文指出,通过以下代码(从上面链接的第二篇文章中提取)创建“Cat”实例将导致每个“Cat”实例指向同一个“哺乳动物”实例

正如指出这一点的文章(上面链接的第一篇)所示,使用此代码会产生以下结果:

var myPet = new Cat('Felix');
myPet.haveABaby();
var myPet2 = new Cat('Garfield');
myPet2.haveABaby();

console.log(myPet2.offspring); // returns ["","",...]
因此,myPet2的子数组中有两个元素。这并不理想,因为它只有一个孩子

这一切对我来说都是有意义的;然而,令人困惑的是,如果您将
this.foundant=[]
更改为
this.foundant=0
,然后将
this.foundant.push(newBaby)
更改为
this.foundant++
,则“Cat”的每个新实例似乎都将其后代属性设置为0

var myPet = new Cat('Felix');
myPet.haveABaby();
var myPet2 = new Cat('Garfield');
myPet2.haveABaby();

console.log(myPet2.offspring); // returns 1
因此,myPet2有1个后代。这是应该发生的事情

为什么每次都会重置
this.founders
,而array/.push()方法不会?我最初的想法是,它与数组被视为对象有关,因此哺乳动物类型的任何“后代”属性都将始终引用同一个对象,而整数是每次重新创建的原始值。是吗


不管是哪种方式,这两种结果在是否为每只新猫创建一个新的哺乳动物实例方面不是相互矛盾吗?

今天已经回答了这个问题,你可以在这里找到:

这些mdn文章看起来不像Mozilla developer network文章,我建议使用这篇文章,因为它不会创建父实例来设置继承的原型部分,也不会修改您不拥有的对象来实现继承的简单任务:


Cat的每个实例都会创建一个具有原型哺乳动物的对象的新实例。请注意,虽然只有一个哺乳动物的实例,但两种猫的
this
是猫的单独实例,而不是哺乳动物。所发生的事情并不是因为两种猫都有一个哺乳动物的原型,而是因为数组是引用,而不是值

因此,虽然有两只猫,而且两只猫
实际上是不同的实例,但它们都共享相同的数组
子代

这说明了在不混淆对象、构造函数和原型的情况下发生了什么:

var a=[];
var b=a;

a.push('hello');

alert(b[0]); // alerts 'hello'.
与javascript中的值(字符串和数字)相比:

引用类型(数组和对象)仅指向基础对象。因此,当引用类型的变量分配给另一个变量时,它们都共享同一个对象(只复制地址,而不复制对象本身)

相反,值类型(数字和字符串)在分配给另一个变量时会被复制


补充答复 这样做的一个微妙的副作用是,您可以通过将其中一只猫重新分配到另一个数组来打破猫之间共享的后代引用:

myPet.offspring = [new Mammal('Kitty'), new Mammal('Spot')];
alert(myPet2.offspring.length); // alerts 0
                                // Because the code above causes the
                                // `offspring` property of myPet to
                                // point to a different array.

对象(数组)是可变的。原语不是。当您将
.push()
放入数组时,您正在变异该数组对象。当您执行
this.foundant++
时,您正在从
.prototype
对象获取
foundant
的值,如果该对象本身不存在该值,则您正在将新值写入
this
,这是您的
myPet
myPet2
对象。所以,是的,你最初的想法是正确的,不,他们彼此并不矛盾。答案很好。在给定的示例中,简化的示例和关于如何打破模式的说明都非常有用。很抱歉,重复一下,但出于某种原因,SO不允许我编辑评论,尽管它只有4分钟:仅供参考-我发布的第一个链接解释了如何在构造函数运行时为每只猫创建一个独特的Madamal.foundant实例。另一个解决方法是根本不使用真正的继承,而是使用寄生继承(作为mixin/decorators实现的继承)。寄生继承的缺点是,它通过复制每个对象的每个方法的函数对象来浪费内存。如果重新分配对象值,它也不会改变原型(阴影)。它与变异(re)有关assignment.obj.someMember++是一个重新分配。即使a和b是数组,而您使用b++也不会改变a,因为您正在分配b(取消引用它),这是一个很好的调用:MDN引用。原始文章提到引用的文章“链接突出”来自MDN,并不是说它实际上是一篇MDN文章。问题已经更新。
var a=0;
var b=a;

a++;

alert(b); // alerts 0
myPet.offspring = [new Mammal('Kitty'), new Mammal('Spot')];
alert(myPet2.offspring.length); // alerts 0
                                // Because the code above causes the
                                // `offspring` property of myPet to
                                // point to a different array.