2012-02-16 7 views
8

を保持し、私はここにJavaScriptでの継承を使用しようとしている、と私は子供クラスによって継承されクラスの配列値を持つ問題を発見しました。コードの下には、通常の継承である:Javascriptの継承:親の配列変数に値

var Parent = function() { 
    this.list = []; 
}; 

var Child = function() {}; 

Child.prototype = new Parent; 
Child.prototype.constructor = Child; 

var obj1 = new Child; 

obj1.list.push("hello"); 

console.log(obj1.list); // prints ["hello"]; 

私はOBJ1に(リストという名前の配列変数を含む親を継承する)新しい子供オブジェクトを初期化し、値をobj1.listをプッシュしようとしたときに " hello "、obj1.list print [" hello "] ..これまでのところとても良いです。

問題は、私は上記の例を行なったし、私は、値 『さようなら』とobj2のさんリストを押しobj2がに新しいオブジェクトを初期化しようとしたときに来て、obj2.list ["hello"、 "goodbye"]が表示されます。 (:)

var obj2 = new Child; 

obj2.list.push("goodbye"); 

console.log(obj2.list); // prints ["hello", "goodbye"]; 

以下のコードを参照してください。私はここで誤解があるかもしれませんが、リスト配列は何とか値を保持して、私は理由を知りません。

私は上記のケースのような他の多くの子クラスにクラスを再利用する場合は、その子クラスに共有配列変数を持っている場合、値が他の子に共有されるので、これは大きな問題です私にとっては予期せぬことです。その後、私はobj2のに新しいオブジェクトを初期化

私は何を期待することは、子供クラスは新しいオブジェクトを表している子供クラスはOBJ1に初期化されたときに、同じことがクラスに行き、の場合、obj1のプッシュ値はobj2と共有しないでください。

- 質問 -

誰でも例のリストの配列変数)が上記の値は、/託児によって開始された値を共有して保持し、なぜ私が出て見つけることができますオブジェクト(上記の場合、obj1およびobj2)?

この問題を解決する別の解決策がある場合は、非常にうれしいですが、上記の問題を最初に見つけてうれしく思います。

+0

両方のインスタンスが同じ「Parent」インスタンスをプロトタイプとして参照し、したがって同じ配列を参照するためです。ここでは多くのJavaScript OOPに関する質問がありました。 –

+0

可能な複製の[Ondesrtanding JavascriptのOOP:他のインスタンスを変更するインスタンス](http://stackoverflow.com/questions/4541788/undesrtanding-javascripts-oop-instances-modifying-other-instances)と[Javascriptのプロトタイプのプロパティが期待どおりに機能しない配列とオブジェクトフィールドを持つ](http://stackoverflow.com/questions/8230085/javascript-prototype-property-not-working-as-expected-with-array-and-object-fiel) –

答えて

12

子オブジェクトにプロトタイプオブジェクトから継承されたプロパティがある場合、実際には子が参照のプロトタイプにプロパティを含むことがあります。子供はそれ自身のコピーを持っていません。だから、両方の子供たちは同じ配列  —を使っています。Child.prototypeに割り当てられた(1つの)Parentプロトタイプオブジェクトのものです。

最初にいくつかの写真を、次にテキストを追加しました。 :-)

new Parent()があなたに与え、この:あなたがChild.prototypeに割り当てる

+-----------------+ 
| Parent instance | 
+-----------------+ 
| list = []  | 
+-----------------+

...。

その後、new Child()はあなたにこれを与える:もう一度new Child()を行う

+------------------+ 
| Child instance 1 | 
+------------------+  +-----------------+ 
| (prototype)  |------->| Parent instance | 
+------------------+  +-----------------+ 
          | list = []  | 
          +-----------------+

はあなたに別のものを提供します:

 
+------------------+  +------------------+ 
| Child instance 1 |  | Child instance 2 | 
+------------------+  +------------------+ 
| (prototype)  |---+ | (prototype)  |---+ 
+------------------+ | +------------------+ | 
         |       | 
         |       |  +-----------------+ 
         +-------------------------+---->| Parent instance | 
                 +-----------------+ 
                 | list = []  | 
                 +-----------------+

あなたが見ることができるように、Childインスタンスのすべてが同じを共有していますParentインスタンス(そのプロトタイプ)に配列があります。

あなたが言う:

var obj = new Child(); 
obj.list.push("foo"); 

は...ここにそれがobj.listを見たときに、JavaScriptエンジンが何をするかです:

  1. objlistと呼ばれるその自身性質を持っているかどうかを確認します。そうでない場合は、
  2. objのプロトタイプの自身のプロパティがlistとなっているかどうかを確認します。そうでない場合は、
  3. objのプロトタイプのプロトタイプが、自身のというプロパティ(list)を持っているかどうかを調べます。
  4. プロトタイプがなくなるまであなたのケースでは

obj自身list性質を持っていないので、エンジンは、この場合には財産を持っているそのプロトタイプ(あなたがChild.prototypeに割り当てParentインスタンス)、に見えます。それが使用されるように。子供にコピーされていないか、に使用されています。そしてもちろん、それは配列なので、配列上の何かを押すと実際に配列にプッシュされます。

あなたはobj.listに何かを割り当てるにした場合、objはチェーンを壊し、その自身list財産になるだろう。したがって、this.list = [];Childに入れると、各子に独自のリストが与えられます。

プロトタイプにオブジェクト参照がある場合はいつでも、このオブジェクトが修正可能なタイプ(「変更可能な」オブジェクト)であることがわかります。配列、日付、プレーンオブジェクト({})、RegExpsなど、それらはすべて状態を持っていて、それらはすべて変更することができるので、それらと一緒に見ることができます。(Stringのインスタンスは不変なので、この同じことが起こりますが、文字列を変更することができないので、その結果は表示されません)。

プリミティブを使用すると、プリミティブを継承しても、 を別の状態の新しいプリミティブに置き換えることができるため、この同じ効果は表示されません。したがって、objがそのプロトタイプからfooプロパティを継承し、foo42である場合、alert(obj.foo)はプロトタイプから値を取得し、 "42"を表示します。 fooを変更する唯一の方法は、または同様の  —であり、obj自身コピーfooとなり、プロトタイプのコピーとは異なります。 (これは++--のようなものを使用しても同じです(例えば、++obj.foo)。実際にはobj.foo = obj.foo + 1と評価されます。

+5

あなたのすばらしいアスキーグラフィックスはもっと保証する+1以上だけど、それはすべて私が報奨することが許されている –

+0

私はいくつかのASCIIアートを愛しています。 ;-) –

+0

同意! ASCIIアートの場合+1。 – pete

3

ここで紛失していることは、Parent.listが1回だけインスタンス化されていることです。そのプロトタイプをChild.prototypeにコピーすると

JavaScriptで「サブクラス」をインスタンス化すると、ではなく、が自動的に親コンストラクタを呼び出します。したがって、Childのすべてのインスタンスは、配列の同じインスタンスを共有します。

親のコンストラクタは、あなたの病気のために求める治療する必要があります呼び出すためにChildのコンストラクタを変更:

明らか
var Child = function() { 
    Parent.call(this); 
}; 

既に件名にいくつかの知識を持っているが、もし興味があるなら、ここです読む価値トピックに関するいくつかのテキスト:

+0

ありがとうnikc!正直なところ、サンプルコードはこの2番目の問題を実際に解決するものです。私はこの質問につながる.callを使うのを忘れていました。 –

+0

サンプルコードは、私にとっても非常に便利でした! –

関連する問題