1

以下の「Cat.prototype=new Mammal()」行がCat()関数内では機能しないのに、Cat()関数外では機能するのはなぜですか?

function Mammal() {
  Mammal.prototype.hasHair = function() { return true; }
}

alert( new Mammal().hasHair() ); // dispays true here

function Cat() {
  Cat.prototype = new Mammal();
}

try {
  new Cat().hasHair(); // doesn't work. why?
} catch ( err ) {
  alert('error'); // displays error here
}

Cat.prototype = new Mammal(); // same line as inside Cat() function

alert( new Cat().hasHair() ); // now it works

私はこれをいくつかの異なるjavascript実装で試したので、実装の特異性ではないと思います。これは主に好奇心からだと思いますが、組織のためだけに、Catの機能内の猫について定義したいと思います。どこにでも広がっているわけではありません。

4

6 に答える 6

3

を実行するnew Catと、から継承する空のオブジェクトCat.prototypeが作成されます。何Cat.prototypeですか?まだ変更していないため、空のオブジェクトです。

コンストラクター内で、を割り当ててCat.prototype = new Mammal();います。の次のインスタンスはCat、このインスタンスから継承しMammalます。Mammalただし、2番目のインスタンスを作成すると、3番目のCatインスタンスが継承する別のインスタンスも作成され、以下同様に続きます。

最終的にこの継承チェーンになります(はのcatXx番目のインスタンスでCatありmammalX、はのx番目のインスタンスですMammal):

cat0 -> Object
cat1 -> mammal0 -> Mammal.prototype
cat2 -> mammal1 -> Mammal.prototype
...

Catインスタンスには独自のプロトタイプがありますが、これはプロトタイプの使用目的(共通コードの共有)とはまったく逆です。それほど大したことではないように見えるかもしれませんが、次のようないくつかの結果があります。

  • メモリ使用量の増加:(ほぼ)各Catインスタンスには対応するMammalインスタンスがあるため、2Nの代わりにオブジェクトがありますN + 1
  • 継承テストは壊れます:instanceofプロトタイプを比較することによって機能し、のプロトタイプは(複数のインスタンスを作成した後)cat0とは異なるため、を生成します。Cat.prototypecat instanceof Catfalse

本当に必要な継承チェーンは次のとおりです。

cat0 -> Mammal.prototype
cat1 -> Mammal.prototype
cat2 -> Mammal.prototype
...

コンストラクター関数内でプロトタイプに値を割り当てたり(またはプロトタイプを上書きしたり)しないでください。コンストラクター関数は、インスタンス固有のプロパティのみを設定する必要があります(静的プロパティを変更したい場合もありますが、これらはあまり一般的ではありません)。

コンストラクター関数の外部でプロトタイプをセットアップします。

function Mammal() {}       
Mammal.prototype.hasHair = function() { return true; }

function Cat() {
    Mammal.apply(this, arguments);
}
Cat.prototype = Object.create(Mammal.prototype);
Cat.prototype.constructor = Cat;

Object.create引数として渡したオブジェクトを継承するオブジェクトを作成します。MDNを参照してください。

于 2012-10-29T00:53:48.747 に答える
2

コンストラクターが実行されるprototypeに、メンバーがオブジェクトインスタンスに渡されるためです。

http://jsfiddle.net/vSDbB/

function Mammal() {
  Mammal.prototype.hasHair = function() { return true; }
}

function Cat() {
    Cat.prototype = new Mammal();
}

new Cat();
alert(new Cat().hasHair());

new Cat();したがって、上記のコードは機能しますが、最初の行にコメントした場合、そのnew Cat().hasHair()行にはCatのプロトタイプにhasHair()メソッドがないため、機能しません。

于 2012-10-29T00:40:20.283 に答える
1

インスタンス化のたびにプロトタイプを上書きしているためです。Javascriptには実際には継承がありません。

あなたがするとき

function Mammal() {
    Mammal.prototype.hasHair = function() { return true; }
}
new Mammal().hasHair();

その意味は

  1. Mammalコンストラクターとして使用し、new Mammal型オブジェクトをインスタンス化します
  2. Mammalこれを行うたびにのプロトタイプを上書きします

しかし、これを行う場合:

function Cat() {
}
Cat.prototype = new Mammal();
new Cat().hasHair();

その意味は

  1. コンストラクCatターのプロトタイプをnew Mammal type object
  2. 'クラス'を介してnew Cat type object'継承された'メソッドをインスタンス化しますhasHair()Mammal

お役に立てれば。

于 2012-10-29T01:03:40.020 に答える
1

少し混乱しています。あなたは実際に何を期待していますか?

からプロトタイプチェーンに関数を割り当てていますが、コンストラクター関数の呼び出しで割り当てています。のように呼び出すと、呼び出しのオブジェクトはその関数についてまったく知りません。Catnew Cat().hasHair()

.prototype実際にコンストラクター関数を呼び出す前に、を拡張する必要があります。

于 2012-10-29T00:40:35.573 に答える
1

目標を達成できるようにコードを編集しました。次のコードは、基本クラスを作成してから、実行しようとしているのと同じスタイルのサブクラスを作成します。

function Mammal() {
  this.hasHair = function() { return true; }
}

alert( new Mammal().hasHair() ); // dispays true here

function Cat() {
   Mammal.call(this);

}

try {
  alert( new Cat().hasHair() ); // dispays true here
} catch ( err ) {
  alert('error'); // should not fire
}

</ p>

于 2012-10-29T00:53:16.473 に答える
0

関数のプロトタイプ(クラスとして機能する)は、新しいオブジェクトが作成されたときにコピーされるものです。コンストラクターでプロトタイプにプロパティを設定するべきではありません。

これのどちらか:

function Mammal() {
}
Mammal.prototype.hasHair = function(){ return true };

またはこれ:

function Mammal() {
    this.hasHair = function(){ return true }
}
于 2012-10-29T00:47:06.040 に答える