只有一个数组,因为您只创建一个。该数组附加到“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()
or时new CDList()
。可以跨实例共享的所有内容都可以附加到原型上。这实质上意味着像“添加”函数这样的属性只被创建一次,然后被所有实例使用(导致原始问题的原因)。
链接原型时,您不能(通常)直接链接它们,例如:
CDList.prototype = List.prototype;
DVDList.prototype = List.prototype;
// Now add a new function to "CDList"
CDList.prototype.Foo = function() { alert('Hi'); };
因为“List”、“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();
这将创建一个“List()”类型的新对象,其特殊功能是“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"
但是,请记住,我们打算使用函数“List()”的主体在每个实例的基础上创建像这个数组“Items”这样的东西?这是您放置任何“构造函数”代码的地方,例如
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”类型的新对象,它没有自己的属性但可以扩展,例如通过一个新的“Add”函数:
CDList.prototype.Add = function() { /* CD specific code! */ };
然而,如果你不给这个新的原型对象添加一个“Add”属性,“ctor()”的原型就会起作用——这就是“List()”的原型。这就是期望的行为。
此外,“List()”中的代码现在仅在您执行此操作new List()
或直接从另一个函数(在子类中通过List.call(this);
)调用它时执行。