javascript - 使用纯原型(prototype)方法的 Javascript 中的寄生继承

标签 javascript oop inheritance

我正在学习 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 所详细描述的。因此,要实现寄生 继承,您必须遵循两条规则。

  1. 切勿直接在原型(prototype)中定义字段成员。
  2. 在实例化后继者时总是调用基础构造函数

后者可以根据个人喜好使用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/

相关文章:

javascript - PWA 离线模式无法从移动浏览器的缓存中加载

c++ - 构造函数中没有匹配的调用函数 - C++ 11

C++ - 派生类 - 错误 : expected primary expression before'int'

javascript - 如何在 Angular 2 中将变量附加到 $scope?

javascript - 如何在 JavaScript 中编写没有 'else' 的 IF else 语句

javascript:如何在函数本身中引用匿名函数?

c++ - 为什么在 C++ 中,Visual Studio 将我的类构造函数描述为具有 "2+ Overloads"?

r - 是否每个 S4 都需要通用

c++ - c++ 中的高性能代码(继承、指向函数的指针、if)

java - java中如何从父类对象加载子对象