12

私は一般的な'List'クラスを作成しようとしています。

  • プロパティ:アイテム-これは「何でも」の配列になります
  • メソッド:Add()-これは抽象的で、特定の'List'オブジェクトによって実装されます
  • メソッド:Count()-「アイテム」の数を返します

次に、「リスト」から継承するサブクラスを作成します。

// Class 'List'
function List(){
    this.Items = new Array();
    this.Add = function(){ alert('please implement in object') }
}

// Class CDList - which inherits from 'List'
function CDList(){
    this.Add = function(Artist){
        this.Items.push(Artist)
    }
}
CDList.prototype = new List();
CDList.prototype.constructor = CDList;

// Create a new CDList object
var myDiscs = new CDList();
myDiscs.Add('Jackson');
myDiscs.Count()  <-- this should be 1


// Create a second CDList object
var myDiscs2 = new CDList();
myDiscs2.Add('Walt');
myDiscs2.Add('Disney');
myDiscs2.Count()  <-- this should be 2

...しかし、これにより、すべての「CDList」インスタンスに対して共有の「Items」リストが作成されるようです。どういうわけか、「CDList」インスタンスごとに「Items」リストの新しい継承インスタンスを作成する必要があります。

これどうやってするの?

*この例では、例として「アイテム」リストを使用しています。サブクラスに、任意のタイプの継承されたプロパティの新しいインスタンスを含めることができるようにしたいと思います。必ずしもArrayオブジェクトである必要はありません。

4

2 に答える 2

15

作成するアレイは 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);) 直接呼び出したときにのみ実行されるようになりました。

于 2012-08-19T14:51:51.763 に答える
5

これを試して:

function CDList(){
    List.call( this )
    this.Add = function(Artist){
        this.Items.push(Artist)
    }
}

スーパーコンストラクターを呼び出す必要があります...

JavaScript の継承に関する MDN ネットワークのこの記事が気に入っています。この方法/テクニックを試してみたところ、テストしたすべてのブラウザー (Chrome、Safari、Internet Explorer 8+、および Firefox) で非常にうまく機能しました。

于 2012-08-19T14:33:05.900 に答える