1

私は最近 Javascript に取り掛かっていて、小さな問題に遭遇するまではプロトタイプ機能を理解していると思っていました。

オブジェクトの2つの新しいインスタンスを作成し、それらの2つのオブジェクトのいずれかで変数の値を変更すると、他のオブジェクトでも変更されることがわかりました。これは、当時私が探していた効果ではなく、配置する必要がありました代わりにコンストラクターのプロパティを使用して、インスタンスごとに個別に保持します。

各オブジェクト インスタンスがプロパティ (文字列、ブール値など) を共有する必要があるのはいつですか?

プロトタイプは本当に関数を宣言するためにのみ使用されますか? コンストラクターですべてのプロパティを宣言しますか?

例:

function Animal(name) {
    this.name = name;
}

Animal.prototype = {
    color: null,
    walk: function() {}
};

var dog = new Animal('Sam');
var horse = new Animal('Bob');

dog.color = 'black';
horse.color = 'chestnut';

プロパティがプロトタイプにあり、両方のインスタンス間で共有されているため、「犬」オブジェクトの色が「栗」になりました。この効果が望まれるのはどこでしょうか? 一見すると、これからわか​​るのはデバッグの頭痛の種だけですか、それともJavascriptでこれをうまく利用できますか? 悪い世界的な変化を思い出させます。

ほとんどの場合、1 つのインスタンスでプロパティを変更したくないが、他のすべてのインスタンスでも変更される場合は、関数を宣言するためにプロトタイプを使用しますか?

4

2 に答える 2

4

失礼ながら、あなたはプロトタイプの振る舞いを誤解していました。
すべてを説明すると長すぎる話になりますが、プロトタイプに設定されたプロパティに関しては、インスタンスのプロパティを変更すると、すべてのインスタンスのこの値が変更されると、バグの無限の原因になります。
jsfiddle、jsbin、cssdeck、または好きなテストサイトを使用して、馬/犬の例を見てください。馬の色を変更しても、犬の色は変更されません。まだ黒いです。

どうしてこんなことに ?
dog のインスタンスがプロパティに対して READ アクセスを行うと、プロトタイプでシークされ、次にプロトタイプのプロトタイプでシークされ、プロトタイプのチェーンの最後に到達するまで続きます。最初に値が見つかった場合はそれが返され、プロトタイプでプロパティが見つからない場合は undefined が返されます。
したがって、インスタンスの作成直後は、すべての動物の色が null になります。
インスタンスのプロパティで WRITE アクセスを行うと、このインスタンスで新しいプロパティが作成され、指定された値が割り当てられます。このようにして、プロトタイプはその参照関数を保持し、インスタンスはその値を取得します。安心。


1) すべてのインスタンスがそれらのプロパティに有効な値 (デフォルト値) を提供することを保証します。
2)すべてのインスタンス間で共有される(静的)プロパティ(さらに重要なことに、大きなオブジェクト)を一度割り当てることで、メモリを節約できます。Expl : 犬のデフォルト画像。
3) すべてのインスタンスの値を一度に変更したい場合があります。この場合、プロトタイプの値を変更すると、まさにそれが行われます。
4) クラス (C++ の古典的な意味で) を JavaScript クラスに関連付けることで、JavaScript インタープリターがコードを最適化できるようにします。バックグラウンド クラスを使用してインスタンスを表現し続けます。パフォーマンスが重要な場合、それは非常に重要なポイントです。そうでない場合は、忘れてください:-)

ほんの小さな例です:動物を定義すると、各インスタンスにはその色と名前があります:プロトタイプではなく、コンストラクターでそれらを正しく設定することは理にかなっています。ただし、たとえば足の数については、インスタンスから別のインスタンスに変更されることはありません。また、子クラスを作成する場合は、子のプロトタイプの値をいつでも変更して、子クラスにこの数を変更させることができます。

function Animal(name, color) {
    this.name = name;
    this.color = null;
}    
Animal.prototype.legCount = 4; // most animal have 4 legs (...)

// Dog Class, inheriting from Animal
function Dog(name) {    Animal.apply(this, arguments);    }
// Set Animal as Dog's prototype's prototype.
// so that we can safely change Dog's prototype.
Dog.prototype = Object.create(Animal.prototype); 

// Duck class
function Duck(name) {    Animal.apply(this, arguments);    }
// same inheritance scheme
Duck.prototype = Object.create(Animal.prototype);
// ... but we change the legCount, for the ducks only
Duck.prototype.legCount = 2;

高度な回答:

インスタンスでこのプロパティに値を割り当てようとしても、プロトタイプ プロパティを変更しないでおきたい場合は、プロトタイプで読み取り専用プロパティとして設定します。

Object.defineProperty(Duck.prototype,'legCount', { get : function() { return 2 } } );

var duck = new Duck(...) ;
duck.legCount = 5;
console.log (duck.legCount) ; // --> output is still 2

インスタンスの値を設定してすべてのインスタンスの値を変更したい場合 (プロトタイプのプロパティ値を明示的に変更せずに)、プロトタイプのプロパティ定義のセッターで実行します。

 // using a closure
 var duckLegCount = 2;
 Object.defineProperty(Duck.prototype,'legCount', { 
                            get : function()   { return duckLegCount } ,
                            set : function (x) { duckLegCount = x    }    } );
 var duck1 = new Duck(...);
 var duck2 = new Duck(...);
 duck1.legCount = 12;
 console.log ( duck2.legCount ) ;  // output is 12
于 2013-09-06T23:59:01.533 に答える
3

プロトタイプに変数を割り当てるべきではありません (実際にはAnimal.prototype、カスタム オブジェクトで上書きしています)。インスタンス プロパティを定義するには、次のようにコンストラクタ関数で定義します。

function Animal(name) {
    this.name = name;
    this.color = null;
}

// Instance method
Animal.prototype.walk = function() {};

// Static method
Animal.run = function() {};


// Inheritance example
function Dog(name) {
    // Call parent constructor
    Animal.apply(this, arguments);
}

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.walk = function() {
    // Call parent method (if you need)
    Animal.prototype.walk.apply(this, arguments);
    ...
};
于 2013-09-06T22:27:35.500 に答える