如何通过原型实现经典类继承

时间:2015-12-18 15:57:28

标签: javascript class inheritance prototype

我想在JS中实现以下行为。 请注意,语法是符号。

这是我的父类

class = TList {
   FList: array;

   function AddElement(Ele) {
        Flist.Add(Ele)
   };

   function RemoveEle(Ele) {
        FList.Remove(Ele)
   };   
}

现在我要继承这个班级。我的子类应该自动拥有父类的所有属性,并且应该能够扩展它们而不需要重写代码。

class = TAlertList(inherit from TList) {

   function AddElement(Ele) {
      Alert('element will be added');
      call.parent.AddElement(Ele)
   };

   function RemoveElement(Ele) {
     call.parent.RemoveElement(Ele);
     Alert('element removed');
   }

}

请注意我如何在我希望的地方继承父方法。

现在我应该可以从我的子类创建一个对象并执行以下操作。

MyAlertList = new TAlertList;
MyAlertList.Add('hello');
console.log(MyAlertList.FList);

我应该能够从TAlertList继承更多子类,并能够更改现有行为。我需要在纯ES5中执行此操作而不使用任何库。预计会有标准的OOP实践。

3 个答案:

答案 0 :(得分:1)

您的代码如下

function TList(){
  this.FList = [];
}

TList.prototype.AddElement = function(Ele){
 this.FList.push(Ele);
}
TList.prototype.RemoveElement = function(Ele){
 this.FList.splice(Ele,1); //Ele is the index to remove;
}

这是了解继承在JavaScript中如何工作的近似值。

function TAlertList (){
     TList.call(this);
}

TAlertList.prototype = Object.create(TList.prototype);
TAlertList.prototype.constructor = TAlertList;

TAlertList.prototype.AddElement = function(ele){
   alert('Element will be added');
   TList.prototype.AddElement.call(this,ele);
};
TAlertList.prototype.RemoveElement = function(ele){
   alert('Element will be remove');
   TList.prototype.RemoveElement.call(this,ele);
};

所以,经典的超级电话是

ParentClass.prototype.myMethod.call(this,args);

答案 1 :(得分:0)

这是Q& A

编辑 - 如果您打算阅读全文,请不要忘记阅读@ paul的评论。

几乎所有答案都是基于受欢迎的#34; Person"关于JS OOP的MDN文档中的示例。

此方法背后的理论是将对象的 fields 放在构造函数中,同时在原型对象中实现方法。子对象可以通过使用设计的this值调用构造函数来继承所有字段。此外,它还可以继承所有方法,方法是将父对象的原型对象与原型对象相同。唯一的规则是,在实现继承时,需要使用callapply调用方法将方法指向正确的this对象。

我不喜欢这种方法有两个原因。

  1. 对象的字段方法必须在两个对象之间分开(字段 - 构造函数,方法 - 原型)。这并不具有独特实体的独特行为的风格 - 应该是单个类。

  2. 继承时必须指定父对象的名称。这不是自动的。让我们说对象 C 继承自对象 A 。因此,在对象 C 的方法中,您需要在继承对象 A 时提及它。 (TList.prototype.AddElement.call(this, Ele);)如果对象 B 稍后出现怎么办?您必须将 C 的所有继承方法更改为从 B 进行调用。这绝不是接近良好的继承。

  3. 我想在MDN方法中克服这些问题。我提出了以下模型,没有this没有call而没有apply。代码简单易懂。在继承父对象时,您不必提及父对象的名称。因此,一个新的课程可以随时进入父母和孩子之间,而不需要太多的改变。请讨论这个,并指出这个模型的弱点。

    这是返回父对象的函数。

    function tList() {
    
       var ret = Object.create(null);
    
       ret.list = [];
    
       ret.addElement = fucntion(ele) {
          ret.list.push(ele)
       };
    
       return ret;
    
    }
    
    
    //You can create tList object like this: 
    var myList = tList();
    myList.addElement('foo');
    

    现在出现了子对象:

    function tAlertList() {
    
       var ret = Object.create(tList());
    
       //Lets inherit with the fashion of overriding a virtual method
       ret.addElement = function(ele) {
    
          //Here is new code
          alert('Adding element ' + ele);
    
          //Automatic inheritance now
          Object.getPrototypeOf(ret).addElement(ele);
       }
    
       return ret;
    }
    
    //Just create the child object and use it
    var myAlertList = tAlertList();
    myAlertList.addElement('buzz') ;
    

    你可以让大孩子对象并从父母继承而不提及他们的名字。我们假设您必须将tCustomList放在tListtAlertList之间。您所要做的就是告诉tAlertList在一个地方继承tCustomList。 (var ret = Object.create(tCustomList());)其他一切都保持不变。

    这是fiddle

答案 2 :(得分:-1)

使用纯ES5,您可以这样做:

function TList() {
   this.FList = []; //not really ideal.
}

TList.prototype.addElement = function(ele) {
   this.FList.push(ele);
};

TList.prototype.removeElement = function(ele) {
   this.FList.splice(this.FList.indexOf(ele), 1);
}

function TAlertList(){
    this.FList = [];
}

TAlertList.prototype = new TList(); //inherit from TList
TAlertList.prototype.constructor = TAlertList; //reset constructor

TAlertList.prototype.addElement = function(ele) {
  alert('element will be added');
  TList.prototype.addElement.call(this, ele);
};

TAlertList.prototype.removeElement = function(ele) {
  alert('element removed');
  TList.prototype.removeElement.call(this, ele);
};

几个笔记:

除非被覆盖,否则其父级的FList属性实际上将在从父级继承的所有对象之间共享。这意味着如果你不覆盖FList,你就会得到这个:

var a = new TAlertList();
var b = new TAlertList();

a.push(1); //b.Flist === [1]

在我看来,最好将你的孩子的名字命名为与父母不同的其他名字。这样你就不需要这样做了:

TList.prototype.function.call(this, parameter1, parameter2, ..);

你可以像这样打电话给他们:

this.function(paremeter1, parameter2);

当然,它不是一种静态的方式来调用父级,因为你可以用你自己的方式覆盖这个函数。然后再次TList.prototype.function不需要拥有该函数的对象的父级的功能。为此,您需要使用非标准的ES5 __proto__属性。

this.__proto__.function.call(this, parameter1, parameter2, ..);

除非你打算在不同的物体周围使用这个功能,否则你不需要它。