How to implement classical class inheritance throu

2019-05-13 19:37发布

I would like to implement the following behavior in JS. Please note that the syntax is symbolic.

This is my parent class

class = TList {
   FList: array;

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

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

Now I'm going to inherit from this class. My child class should automatically have all the properties of the parent and should be able to extend them without rewriting the code.

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');
   }

}

Please note how I inherit the parent methods at places I wish.

Now I should be able to create an object from my child class and do the following.

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

I should be able to inherit more child classes from TAlertList and be able to change the existing behavior. I need to do this in pure ES5 without using any libraries. Standard OOP practices are expected.

4条回答
我想做一个坏孩纸
2楼-- · 2019-05-13 20:00

This is Q&A

Edit - Don't forget to read @paul's comment too if you planning to read the full text.

Almost all the answers came in were based on the popular "Person" example in the MDN documentation about JS OOP.

The theory behind this method is to put the fields of the object inside a constructor function while implementing the methods in a prototype object. A child object can inherit all the fields by calling the constructor function with a contrived this value. Also it can inherit all the methods by having the same prototype object of the parent as it's prototype object too. The only rule is that you need to call the methods using call or apply to point the methods to the correct this object when implementing inheritance.

I didn't like this approach for two reasons.

  1. The fields and methods of an objects has to be separated between two objects (fields - constructor function, methods - prototype). This doesn't have the flavor of a unique behavior of a unique entity - which should be a single class.

  2. You have to specify the name of the parent object when inheriting. This is not automatic. Let's say object C inherits from the object A. So, inside the methods of the object C, you need to mention object A when inheriting from it. (TList.prototype.AddElement.call(this, Ele);) What if object B comes in between later on? You will have to change all the inheriting methods of C to call from B. This is in no way near good inheritance.


I wanted to overcome these problems in MDN method. I came up with the following model with no this no call and no apply. Code is simple and easy to follow. You don't have to mention the name of the parent object when inheriting from it. So, a new class can come in between the parent and the child at any time without too many changes. Please discuss this and point out the weaknesses of this model.

Here is the function which returns the parent object.

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');

Now comes the child object:

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') ;

You can have grand children object and inherit from parent without mentioning their names. Let's say you have to put tCustomList between tList and tAlertList. All you have to do is to tell the tAlertList to inherit from tCustomList in a single place. (var ret = Object.create(tCustomList());) Everything else remain the same.

Here is the fiddle.

查看更多
趁早两清
3楼-- · 2019-05-13 20:01

Please note that the TList constructor should be applied to the TAlertList instance;

ES5, first set up the base constructor

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

TList.prototype = {
    constructor: TList,
    AddElement: function AddElement(Ele) {
        this.Flist.push(Ele);
    },
    RemoveEle: function RemoveEle(Ele) {
        var i = this.Flist.lastIndexOf(Ele);
        if (i !== -1)
            this.Flist.splice(i, 1);
    }
};

Next set up the constructor which extends it, see how this means calling the base constructor on the instance being created by the extended constructor and creating a prototype object which inherits the prototype of the base constructor

function TAlertList() {
    // construct from base
    TList.call(this);
    // further construct
    // ...
}
TAlertList.prototype = Object.create(TList.prototype);
TAlertList.prototype.constructor = TAlertList;

// depending on how you want to reference stuff
TAlertList.prototype.AddElement = function AddElement(Ele) {
    alert('element will be added');
    TList.prototype.AddElement.call(this, Ele);
};
TAlertList.prototype.RemoveElement = function RemoveElement(Ele) {
    TList.prototype.RemoveEle.call(this, Ele);
    alert('element removed');
};

ES6 syntax makes use of the super keyword

class TList {
    constructor() {
        this.FList = [];
    }
    AddElement(Ele) {
        this.Flist.push(Ele);
    }
    RemoveEle(Ele) {
        var i = this.Flist.lastIndexOf(Ele);
        if (i !== -1)
            this.Flist.splice(i, 1);
    }
}

class TAlertList extends TList {
    constructor() {
        super();
    }
    AddElement(Ele) {
        alert('element will be added');
        super.AddElement(Ele);
    }
    RemoveElement(Ele) {
        super.RemoveEle(Ele);
        alert('element removed');
    }
}

Back to ES5, generalising as a factory so you can see a sort of algorithm of how to do it

function extend(baseConstructor, extendedConstructor, prototypeLayer) {
    function Constructor() {
        var i = 0, j = 0, args = Array.prototype.slice.call(arguments);

        i = j, j += baseConstructor.length;
        baseConstructor.apply(this, args.slice(i, j));

        i = j, j = args.length;
        extendedConstructor.apply(this, args.slice(i, j));
    }
    Object.defineProperty(Constructor, 'length', { // fix .length
        value: baseConstructor.length + extendedConstructor.length,
        configurable: true
    });
    Constructor.prototype = Object.create(baseConstructor.prototype);
    Constructor.prototype.constructor = Constructor;
    Object.assign(Constructor.prototype, prototypeLayer);
    return Constructor;
}

So then

function Foo(x) {this.foo = x;}
Foo.prototype.fizz = 1;
var Bar = extend(Foo, function (x) {this.bar = x;}, {buzz: 1});
// ...
var b = new Bar('foo', 'bar');
b.foo; // "foo"
b.bar; // "bar"
b instanceof Foo; // true
b instanceof Bar; // true
b.fizz; // 1
b.buzz; // 1

Please note that this is an example of the algorithm you should be following when you write each extended constructor, not production code

查看更多
太酷不给撩
4楼-- · 2019-05-13 20:01

Your code would be the following

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;
}

This is an approximation to know how the inherit works in 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);
};

So, the classic super call is

ParentClass.prototype.myMethod.call(this,args);
查看更多
相关推荐>>
5楼-- · 2019-05-13 20:03

With pure ES5, you could do it like this:

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);
};

Couple of notes:

The FList property of its parent will actually be shared amongst all objects that inherit from the parent unless overwritten. That means that if you don't overwrite FList you'll get this:

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

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

In my opinion, it would be best if you named your children functions with other names different from the parent. This way you don't need to do:

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

You can just call them like this:

this.function(paremeter1, parameter2);

Of course, it's not a static way to call the parent as you can overwrite the function with your own. Then again TList.prototype.function isn't necessary the function of the parent of the object that owns the function. For that you'd need to use non-standard ES5 __proto__ property.

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

Unless you plan on juggling the function around different objects, you don't need that.

查看更多
登录 后发表回答