在 JavaScript 中实现继承,是引用父级原型还是复制父级原型更好?

4

目前我在JavaScript库中实现继承的方式如下:

parent = function() { };
parent.prototype.hello = function() { alert('parent'); };

child = function() { };
child.prototype = parent.prototype;

然而,我注意到当我在子“类”的原型中覆盖一个函数时,它会不希望地同时覆盖父级的原型:

child.prototype.hello = function() { alert('child'); }

(new parent()).hello();  // alerts "child" --> undesirable
(new child()).hello();   // alerts "child" --> desirable

如果我从子原型中删除了某些内容

delete child.prototype.hello;

那么,如果更改了父级的原型,则会产生不良影响。因此,也许

child.prototype = parent.prototype;

这种实现继承的方式不是最好的吗?也许将父原型对象复制到子原型对象中,比让子"类"引用父原型对象更有意义?

_.extend(child.prototype, parent.prototype);

1
是的,你说得对。孩子们和父母闹矛盾并不好。适当的扩展是正确的方法。 - Igor Milla
4
我还没有看到过 child.prototype = parent.prototype; 正确的情况。你可能想要使用 child.prototype = Object.create(parent.prototype); - apsillers
3
请使用Object.create进行继承。请参阅使用 Object.create 进行继承的优势和https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Inheritance。 - Felix Kling
这不是同样的问题。他们询问是否要创建一个新的父对象实例作为子对象的原型,而我询问是否要复制父对象的原型。 - Chad Johnson
这里解释了构造函数、原型、继承和混入的常见模式:https://dev59.com/J2Qo5IYBdhLWcg3wbe5K#16063711 - HMR
1个回答

10
"child.prototype = parent.prototype;" 这个写法是不正确的,原因就像你在问题中详细说明的那样。
使用 _.extend 也不是你想要的。请考虑一下,在“继承”的父级原型属性中进行更改将不会导致子级发生更改:
// perform extension
_.extend(child.prototype, parent.prototype);

// parent later gains new property after extension
parent.prototype.parentProp = function() { alert("new"); };

(new parent()).parentProp();  // alerts "new" --> desirable
(new child()).parentProp();   // error; doesn't exist --> undesirable

你可能需要 child.prototype = Object.create(parent.prototype); 这句话的意思是:“在 child.prototype 中存储一个新对象。这个新对象使用 parent.prototype 对象作为它自己的原型。” 这将导致真正的继承发生,因为当一个 child 实例想要一个属性时,它首先查找它自己的属性,然后是它的直接原型 (child.prototype),最后是它的原型的原型 (parent.prototype)。 原型链
  • When using child.prototype = parent.prototype, the prototype chains for the instances look like:

    { child instance } ==> { only prototype }
    
    { parent instance } ==> { only prototype }
    

    where only prototype is the shared object referred to by both child.prototype and parent.prototype.

  • When using _.extend(child.prototype, parent.prototype), the child prototype and parent prototypes are different, but there's no direct inheritance. Changing the parent prototype doesn't change the child at all, since we merely copied the parent prototype's properties into the child prototype at one single point in time.

    { child instance } ==> { child.prototype }
    
    { parent instance } ==> { parent.prototype }
    
  • With child.prototype = Object.create(parent.prototype); you actually have inheritance happening from parent to child prototype:

    { child instance } ==> { child.prototype } ==> { parent.prototype }
    
    { parent instance } ==> { parent.prototype }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接