I\'m trying to have a generic \'List\' class, which will have:
Try this:
function CDList(){
List.call( this )
this.Add = function(Artist){
this.Items.push(Artist)
}
}
You need to call the superconstructor...
I like this article of the MDN network about JavaScript inheritance. I tried this method/technique and it works very fine in all browsers I tested (Chrome, Safari, Internet Explorer 8+, and Firefox)..
There is only one Array because you only create one. This array is attached to the prototype of "CDList" and therefore shared between all instances.
To solve this problem: don't attach it to the prototype, but to the instance. This can only be done at construction time:
// This is the constructor of the parent class!
function List() {
this.Items = new Array();
}
// Add methods to the prototype, not to the instance ("this")
List.prototype.Add = function() { alert('please implement in object'); };
// Constructor of the child
function CDList() {
List.call(this); // <-- "super();" equivalent = call the parent constructor
}
// "extends" equivalent = Set up the prototype chain
// Create a new, temporary function that has no other purpose than to create a
// new object which can be used as the prototype for "CDList". You don't want to
// call "new List();", because List is the constructor and should be called on
// construction time only. Linking the prototypes directly does not work either,
// since this would mean that overwriting a method in a child overwrites the
// method in the parents prototype = in all child classes.
var ctor = function() {};
ctor.prototype = List.prototype;
CDList.prototype = new ctor();
CDList.prototype.constructor = CDList;
// Overwrite actions
CDList.prototype.Add = function(Artist) {
this.Items.push(Artist);
};
Demo: http://jsfiddle.net/9xY2Y/1/
The general concept is: Stuff that each instance must have its own copy of (like the "Items" array in this case) must be created and attached to "this" (= the instance) at construction time, i.e. when doing new List()
or new CDList()
. Everything that can be shared across instances can be attached to the prototype. This essentially means that properties like the "Add" function are created exactly one time and are then used by all instances (what caused the original issue).
When linking prototypes, you must not directly link them (usually), e.g.:
CDList.prototype = List.prototype;
DVDList.prototype = List.prototype;
// Now add a new function to "CDList"
CDList.prototype.Foo = function() { alert('Hi'); };
Because the prototypes of the three functions "List", "CDList" and "DVDList" got directly linked to each other, they all point to one prototype object, and that is List.prototype
. So, if you add something to CDList.prototype
you actually add it to List.prototype
- which also is the prototype of "DVDList".
var dvd = new DVDList();
dvd.Foo(); // <-- alerts "hi" (oops, that wasn't intended...)
What does the trick is to link the prototype to a new instance of the parent class:
CDList.prototype = new List();
This creates a new object of type "List()" with the special feature that the prototype of the function "List()" is linked to the new object, enabling you to call properties of the prototype directly on the object:
var l = new List();
alert( l.hasOwnProperty("Add") ); // <-- yields "false" - the object l has no
// property "Add"
l.Add("foo"); // <-- works, because the prototype of "List" has a property "Add"
However, remember that we intended to use the body of the function "List()" to create stuff like this array "Items" on a per-instance basis? It is the place where you put any "constructor" code, e.g.
function User(userId) {
$.getJSON('/user/' + userId, ...
}
function Admin() {}
Admin.prototype = new User( // ... now what?
One very clean solution is to use another function to create a prototype-object:
var ctor = function() {}; // <-- does nothing, so its super safe
// to do "new ctor();"
It is now okay to directly link the prototypes, because we will never add anything to ctor.prototype
:
ctor.prototype = List.prototype;
If we then do:
CDList.prototype = new ctor();
the prototype of "CDList()" becomes a new object of type "ctor", that has no own properties but can be extended, e.g. by a new "Add" function:
CDList.prototype.Add = function() { /* CD specific code! */ };
However, if you do not add an "Add" property to this new prototype object, the prototype of "ctor()" kicks in - which is the prototype of "List()". And that's the desired behavior.
Also, the code in "List()" is now only executed whenever you do new List()
or when you call it directly from another function (in a child class via List.call(this);
).