作成するアレイは 1 つだけなので、アレイは 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'); };
「List」、「CDList」、および「DVDList」の 3 つの関数のプロトタイプは互いに直接リンクされているため、それらはすべて 1 つのプロトタイプ オブジェクトを指します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?
非常にクリーンな解決策の 1 つは、別の関数を使用してプロトタイプ オブジェクトを作成することです。
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);
) 直接呼び出したときにのみ実行されるようになりました。