3

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.

Charlie
  • 22,886
  • 11
  • 59
  • 90

4 Answers4

2

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

Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Thank you for the detailed explanation. I originally posted this to be Q&A though I'm bit late. Please see my answer and tell me what you think of it. http://stackoverflow.com/a/34420032/4185234 – Charlie Dec 22 '15 at 16:16
  • Although I accepted this as the answer I see a great scalability issue in this approach. You would have had to change all down-the-line objects to introduce a new one in the middle of the hierarchy. That means this is not very production-oriented solution. But thanks for your time. – Charlie Jan 14 '16 at 17:27
  • @CharlieH It is true that changing a constructor in the middle can be tedious using this kind of approach, but that is very unusual behaviour; if you will be doing this often consider if you have ordered your inheritance correctly - the oldest in the chain should be the most generic and the newest in the chain should be the most specific. – Paul S. Jan 14 '16 at 18:44
1

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);
caballerog
  • 2,679
  • 1
  • 19
  • 32
0

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.

Charlie
  • 22,886
  • 11
  • 59
  • 90
  • This is a valid way of doing it, but you lose the `instanceof` operator for type checking and you're generating some extra overhead with re-initialising and duplicated methods in memory. I don't see the benefit of going out of your way to avoid `.call`/`.apply` as you have done, though. consider what happens if `tAlertList` did `ret.list = ['new list'];` and then you did an `.addElement` call, you've now got a weird case where `.addElement` doesn't see the expected list. This will probably lead to hard-to-find bugs. – Paul S. Dec 22 '15 at 17:43
  • By doing this all your instances will create an extra object as each instance will have a different prototype. The `list` property isn't owned by the instance but by it's prototype. Not to mention your factory method will produce unique functions instances instead of linking them, `tAlertList().addElement !== tAlertList().addElement`. – MinusFour Dec 22 '15 at 17:47
  • Oh ok. I see. Method duplication is considered "normal" in classical OOP since the compilers can manage the instances behind the scene. But here its a different story. True. – Charlie Dec 22 '15 at 17:52
  • How about the scalability issue in the other model? If you are working on a project which has a series of classes inheriting from each other in a hierarchy and the project is prone to introduce more classes in between (or remove), what is your suggestion for the requirement created to change the parent class name in all the methods? – Charlie Dec 23 '15 at 06:17
-1

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.

MinusFour
  • 13,913
  • 3
  • 30
  • 39
  • `function TAlertList(){ this.FList = []; }` and naming child functions with different names go against the concept of inheritance. In the first case you can do 'TList.call(this)' to prevent the `FList` being shared. – Charlie Dec 18 '15 at 17:00
  • No they don't go against inheritance. Maybe class based inheritance. Javascript wasn't really thought for that, it follows a different kind of inheritance model. Yes you can reuse the constructor, but the constructor might do more than what you'd want. The only reason I'm overwriting the property manually is to make my point. Parent properties aren't copied, they are linked. – MinusFour Dec 18 '15 at 17:10
  • What if you have 500 properties in the parent class? Are you going to declare them all in the child class? This obviously is against any modal of inheritance. – Charlie Dec 18 '15 at 17:25
  • You'd only have this problem if the parent's constructor initialize 500 properties. If I'd design an object like that I would separate that logic in separate function from the constructor (if it's being used for more than that) so it can be called separately. – MinusFour Dec 18 '15 at 17:33
  • If each of your prototype functions manage their own properties then you'd never have this problem. – MinusFour Dec 18 '15 at 17:36
  • The answer doesn't demonstrate such a modal. Reusing the constructor is mush simpler way. – Charlie Dec 18 '15 at 17:49
  • Please see my answer. – Charlie Dec 22 '15 at 16:15