可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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
回答2:
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);
回答3:
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.
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.
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.
回答4:
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.