关于原型继承的误解源于与经典继承相反,基础构造函数不被调用来实例化基础对象的问题。将原型设置为基础对象并不等同于经典继承,因为原型是在实例之间共享的。正如 Jimmy Breck-McKye 详细描述的那样。因此,要实现 parasitic 继承,您必须遵循两条规则。
- 切勿直接在原型中定义字段成员。
- 在实例化后继者时始终调用基本构造函数
后者可以使用Object.create 或将基础对象的实例直接分配给原型来实现。鉴于Base 是一个构造函数,继承代码将如下所示
方式#1
function Base(){
//a new object is created which is assigned to 'this'
//this object has __proto__ === Base.prototype
this.baseMember = 'I am the parent';
}
Base.prototype.baseMethod = function(){
console.log('I am Base');
};
function Successor(){
//a new object is created which is assigned to 'this'
//this object has __proto__ === Successor.prototype
//we override the object's property which is used for prototypal lookup
//we lose members defined in Successor.prototype
this.__proto__ = new Base();
//we add a new property in the inherited object
this.successorMember = 'I am a child';
}
Successor.prototype.successorMethod = function(){
console.log('I am Successor');
};
我们将通过以下方式使用定义的构造函数
var child = new Successor();
//resulting in structure
//child: { //instance of Successor
// successorMember: 'I am a child',
// __proto__: {//instance of Base
// baseMember: 'I am the parent'
// __proto__: {//Base.prototype
// baseMethod : function
// }}}
console.log(child.successorMember);//accessible via direct property
console.log(child.baseMember);//accessible via prototype lookup
console.log('baseMethod' in child);//true, accessible via prototype lookup
console.log('successorMethod' in child);//false, method doesn't exist anywhere in the chain
注意通过Successor.prototype 定义的缺少的successorMethod。发生这种情况是因为我们覆盖了对象的 __proto__ 属性。
方式#2
覆盖__proto__ 属性的另一种方法是调用Object.create。然而这个函数返回一个新对象,因此我们将不得不覆盖Successor构造函数返回的对象
function Successor(){
//a new object #1 is created which is assigned to 'this'
//this object has __proto__ === Successor.prototype
//a new instance #2 of Base is created with __proto__ === Base.prototype
//a new object #3 is created with a __proto__ set to #2
var successor = Object.create(new Base());
//a new property is added to #1
this.neverShowMember = 'I will not exist in resulting object';
//a new property is added to #3
successor.successorMember = 'I am a child';
//return of a non-primitive type object from constructor overrides the result
return successor;//return object #3
}
让我们详细研究一下这种方法的用法:
var child = new Successor();
//child: { //instance of Object
// successorMember: 'I am a child',
// __proto__: {//instance of Base
// baseMember: 'I am the parent'
// __proto__: {//Base.prototype
// baseMethod : function
// }}}
console.log(child.successorMember);//accessible via direct property
console.log(child.baseMember);//accessible via prototype lookup
console.log('baseMethod' in child);//true, accessible via prototype lookup
console.log('successorMethod' in child);//false, method doesn't exist anywhere in the chain
产生的行为几乎相同。请注意缺少的neverShowMember,尽管它是在构造函数中为this 定义的。这可能是错误的根源。
方式#3
另一种继承方式是不要乱用 proto 链。这种方法在 Jimmy Breck-McKye 的文章中有所描述。我将跳过之前提供的详细 cmets,并将重点关注更改
function Successor(){
//a new instance Base is created with __proto__ === Base.prototype
var successor = new Base();
//extend object with a new property
successor.successorMember = 'I am a child';
return successor;//return instance of Base with extra properties
}
var child = new Successor();
//child: { //instance of Base
// successorMember: 'I am a child',
// baseMember: 'I am the parent'
// __proto__: {//Base.prototype
// baseMethod : function
// }}
console.log(child.successorMember);//accessible via direct property
console.log(child.baseMember);//accessible via direct property
console.log('baseMethod' in child);//true, accessible via prototype lookup
console.log('successorMethod' in child);//false, method doesn't exist anywhere in the chain
您会看到架构变得扁平。作为一个明显的结论,如果您覆盖它们,您将无法访问它们。所以如果在 Successor 内部我们定义
successor.baseMember = 'I am already grown enough!';
实例 (child) 将失去对等于“我是父母”的baseIntance.baseMember 的访问权限。与以前的方法相反,它可以通过child.__proto__.baseMember 访问。但我相信这在用 javascript 开发时并不常见,应该在另一个问题下讨论。
注意,在所有情况下,Successor.prototype 中定义的成员都会丢失。您应该注意在 Successor 构造函数中手动复制它们。
结论
我希望这个描述足够清楚,可以理解 CraCrockford 的 object 函数
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
总是需要一个作为参数o 传递的新实例来实现寄生继承。因此它的用法应该如下所示
var child = object(new Base());
child.successorMember = 'I am a child';
同样适用于 OP 的代码。要遵循 寄生 继承,superInstance 每次传递给 Object.create 时都应该是一个新实例。因此它应该是一个工厂函数
var superInstance = function(){
return {
member1: 'superMember1',
member2: 'superMember2'
}
};
var subInstance = Object.create(superInstance());
或构造函数
function superInstance(){
this.member1: 'superMember1',
this.member2: 'superMember2'
};
var subInstance = Object.create(new superInstance());
希望对你有帮助