我正在学习 JavaScript 中的 OOP,并且浏览了很多关于它的帖子。据我所知,道格拉斯克罗克福德开了一个pure prototypal approach继承而不是经典方法。
以下代码取自here实现克罗克福德的方法:
var superInstance = {
member1: 'superMember1',
member2: 'superMember2'
};
var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';
我知道 Crockford 的方法取消了构造函数(如果我错了请纠正我)。这是否意味着使用这种方法初始化对象成员的唯一方法是使用上面代码中所示的对象文字?另外,如何使用这种方法实现寄生继承,以允许父类 (refer this post) 中的共享成员、私有(private)变量和非标量值?
Crockford 在他的文章中提到了“maker 函数”,但没有给出任何示例代码。如果有人可以使用 Crockford 的纯原型(prototype)方法演示寄生继承,那就太好了。
谢谢。
最佳答案
关于原型(prototype)继承的误解源于与经典继承相反的问题,即不调用基构造函数来实例化基对象。将原型(prototype)设置为基础对象并不等同于经典继承,因为原型(prototype)在实例之间共享。正如 Jimmy Breck-McKye 所详细描述的。因此,要实现寄生 继承,您必须遵循两条规则。
- 切勿直接在原型(prototype)中定义字段成员。
- 在实例化后继者时总是调用基础构造函数
后者可以根据个人喜好使用Object.create
或将基础对象的实例直接分配给原型(prototype)来实现。鉴于 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
constructor
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 的文章中描述了这种方法。我将跳过之前提供的详细评论,并将重点放在更改上
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());
希望对你有帮助
关于javascript - 使用纯原型(prototype)方法的 Javascript 中的寄生继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30996891/