对原型继承的误解源于与经典继承相反的问题,即不调用基构造函数来实例化基对象。将原型设置为基础对象并不等同于经典继承,因为原型在实例之间共享。正如 Jimmy Breck-McKye 详细描述的那样。所以要达到寄生继承你必须遵循两条规则。
- 切勿直接在原型中定义字段成员。
- 实例化后继者时始终调用基构造函数
后者可以根据自己的喜好来实现Object.create
或者将基础对象的实例直接分配给原型。鉴于Base
是一个构造函数,继承的代码如下
Way #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
关注失踪者successorMethod
定义通过Successor.prototype
。发生这种情况是因为我们覆盖了__proto__
对象的属性。
Way #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
在构造函数内。这可能是错误的根源。
Way #3
继承的另一种方法是不要乱搞proto链。 Jimmy Breck-McKye 的文章中描述了这种方法。我将跳过之前提供的详细评论,并将重点放在更改上
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 开发时并不常见,应该在另一个问题下讨论。
Note在所有情况下定义的成员Successor.prototype
都迷失了。您应该注意手动将它们复制到Successor
构造函数。
Conclusion
I hope this description was clear enough to understand that CraCrockford's object
function
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());
希望这可以帮助