Ответ 1
Существует только один массив, потому что вы только создаете его. Этот массив привязан к прототипу "CDList" и поэтому разделяется между всеми экземплярами.
Чтобы решить эту проблему: не прикрепляйте ее к прототипу, а к экземпляру. Это можно сделать только во время строительства:
// 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);
};
Демо: http://jsfiddle.net/9xY2Y/1/
Общая концепция такова: Вещь, что каждый экземпляр должен иметь свою собственную копию (например, массив "Items" в этом случае), должен быть создан и привязан к "this" (= экземпляру) во время построения, т.е. при выполнении new List()
или new CDList()
. Все, что можно разделить между экземплярами, можно подключить к прототипу. Это по существу означает, что свойства, подобные функции "Добавить" , создаются ровно один раз и затем используются всеми экземплярами (что вызвало исходную проблему).
При связывании прототипов вы не должны напрямую связывать их (обычно), например:
CDList.prototype = List.prototype;
DVDList.prototype = List.prototype;
// Now add a new function to "CDList"
CDList.prototype.Foo = function() { alert('Hi'); };
Поскольку прототипы трех функций "Список", "CDList" и "DVDList" напрямую связаны друг с другом, все они указывают на один объект-прототип, а это List.prototype
. Итак, если вы добавите что-то в CDList.prototype
, вы фактически добавите его в List.prototype
, который также является прототипом "DVDList".
var dvd = new DVDList();
dvd.Foo(); // <-- alerts "hi" (oops, that wasn't intended...)
Что трюк заключается в том, чтобы связать прототип с новым экземпляром родительского класса:
CDList.prototype = new List();
Это создает новый объект типа "Список()" со специальной функцией, с которой прототип функции "Список()" связан с новым объектом, позволяя вам вызывать свойства прототипа непосредственно на объекте:
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"
Однако помните, что мы намеревались использовать тело функции "Список()" для создания таких вещей, как этот массив "Элементы" для каждого экземпляра? Это место, где вы помещаете какой-либо код "конструктора", например.
function User(userId) {
$.getJSON('/user/' + userId, ...
}
function Admin() {}
Admin.prototype = new User( // ... now what?
Одно очень чистое решение - использовать другую функцию для создания прототипа-объекта:
var ctor = function() {}; // <-- does nothing, so its super safe
// to do "new ctor();"
Теперь можно напрямую связать прототипы, потому что мы никогда ничего не добавим к ctor.prototype
:
ctor.prototype = List.prototype;
Если мы тогда сделаем:
CDList.prototype = new ctor();
прототип "CDList()" становится новым объектом типа "ctor", который не имеет собственных свойств, но может быть расширен, например. новой функцией "Добавить" :
CDList.prototype.Add = function() { /* CD specific code! */ };
Однако, если вы не добавляете свойство "Добавить" к этому новому объекту прототипа, прототип "ctor()" запускается - это прототип "List()". И это желаемое поведение.
Кроме того, код в "List()" теперь выполняется только при выполнении new List()
или когда вы вызываете его непосредственно из другой функции (в дочернем классе через List.call(this);
).