6

違いが頭に浮かんだと思いますが、念のため申し上げます。

Douglas CrockfordのページでJavaScriptのプロトタイプの継承について、彼は言います

プロトタイプシステムでは、オブジェクトはオブジェクトから継承します。ただし、JavaScriptには、その操作を実行する演算子がありません。代わりに、new f()がf.prototypeから継承する新しいオブジェクトを生成するように、new演算子があります。

私は彼がその文で何を言おうとしているのか本当に理解していなかったので、私はいくつかのテストを行いました。主な違いは、純粋なプロトタイプシステムで別のオブジェクトに基づいてオブジェクトを作成する場合、すべての親の親メンバーは、新しいオブジェクト自体ではなく、新しいオブジェクトのプロトタイプ上にある必要があるということです。

テストは次のとおりです。

var Person = function(name, age) {
        this.name = name;
        this.age = age;
}
Person.prototype.toString = function(){return this.name + ', ' + this.age};

// The old way...
var jim = new Person("Jim",13);
for (n in jim) {
    if (jim.hasOwnProperty(n)) {
        console.log(n);
     }
}
// This will output 'name' and 'age'.

// The pure way...
var tim = Object.create(new Person("Tim",14));
for (n in tim) {
    if (tim.hasOwnProperty(n)) {
        console.log(n);
     }
}
// This will output nothing because all the members belong to the prototype.
// If I remove the hasOwnProperty check then 'name' and 'age' will be output.

オブジェクト自体のメンバーをテストするときにのみ違いが明らかになるという私の理解は正しいですか?

4

2 に答える 2

3

編集:この回答は、もともと@jordancpaulの回答に対する回答でしたが、その後修正されました。プロトタイプ プロパティとインスタンス プロパティの重要な違いを説明するのに役立つ回答の一部を残しておきます。

場合によっては、プロパティすべてのインスタンス間で共有されるため、プロトタイプでプロパティを宣言するときは常に十分に注意する必要があります。次の例を検討してください。

Person.prototype.favoriteColors = []; //Do not do this!

Object.createここで、またはを使用して新しい Person インスタンスを作成すると、new期待どおりに動作しません...

var jim = new Person("Jim",13);
jim.favoriteColors.push('red');
var tim = new Person("Tim",14);
tim.favoriteColors.push('blue');

console.log(tim.favoriteColors); //outputs an array containing red AND blue!

これは、プロトタイプでプロパティを宣言してはいけないという意味ではありませんが、宣言する場合は、コードで作業するすべての開発者がこの落とし穴に注意する必要があります。このような場合、何らかの理由でプロトタイプでプロパティを宣言したい場合は、次のようにすることができます。

Person.prototype.favoriteColors = null

コンストラクターで空の配列に初期化します。

var Person = function(name, age) {
    ...
    this.favoriteColors = [];
}

このメソッドを使用するときの一般的なルールは、単純なリテラル プロパティ (文字列、数値、ブール値) の既定値をプロトタイプに直接設定できることですが、オブジェクト (配列や日付を含む) から継承するプロパティはすべて null に設定してから、コンストラクタで初期化されます。

より安全な方法は、プロトタイプでのみメソッドを宣言し、コンストラクターで常にプロパティを宣言することです。

とにかく、質問はObject.createについてでした...

Object.create に渡される最初の引数は、新しいインスタンスのプロトタイプとして設定されます。より良い使用法は次のとおりです。

var person = {
    initialize: function(name, age) {
        this.name = name;
        this.age = age;
        return this;
    },

    toString: function() {
        return this.name + ', ' + this.age;
    }
};

var tim = Object.create(person).initialize("Tim",14);

これで、出力は最初の例と同じになります。

ご覧のとおり、これは Javascript の OOP のより古典的なスタイルとは異なる哲学的アプローチです。Object.create では、コンストラクターではなく、既存のオブジェクトから新しいオブジェクトを作成することに重点が置かれます。その後、初期化は別のステップになります。

個人的には、Object.create アプローチについては複雑な気持ちです。既存のプロトタイプに追加のプロパティを追加するために使用できる 2 番目のパラメーターがあるため、継承には非常に便利ですが、より冗長になり、 instanceof チェックが機能しなくなります (この例の代替手段は check になりますperson.isPrototypeOf(tim))。

Object.create が冗長であると私が言う主な理由は、2 番目のパラメーターのためですが、次のような問題に対処する便利なライブラリがいくつかあります。

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(その他)

それが混乱するよりも啓発的だったことを願っています!

于 2012-11-10T15:12:24.140 に答える
3

あなたの仮定は正しいですが、Douglas があまり話していない別のパターンがあります - プロトタイプはプロパティにも使用できます。person クラスは次のように記述できます。

var Person = function(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.name = null; //default value if you don't init in ctor
Person.prototype.age = null;
Person.prototype.gender = "male";
Person.prototype.toString = function(){return this.name + ', ' + this.age;};

この場合、例のように、このクラスのインスタンスのプロパティを反復処理しても、「性別」プロパティの出力は生成されません。

編集1:コンストラクターでの名前と年齢の割り当てにより、プロパティが hasOwnProperty によって表示されます(これを思い出させてくれた@mattに感謝します) 割り当てられていない性別プロパティは、誰かがインスタンスに設定するまで表示されません。

EDIT 2:これにさらに追加するために、代替の継承パターンを提示します-非常に大規模なプロジェクトで個人的に使用したものです:

var inherits = function(childCtor, parentCtor) {
  function tempCtor() {};
  tempCtor.prototype = parentCtor.prototype;
  childCtor.superclass = parentCtor.prototype; 
  childCtor.prototype = new tempCtor();
  childCtor.prototype.constructor = childCtor;
};

var Person = function(name){
    this.name = name;
}
Person.prototype.name = "";
Person.prototype.toString = function(){
    return "My name is " + this.name;
}

var OldPerson = function(name, age){
    OldPerson.superclass.constructor.call(this);
    this.age = age
};
inherits(OldPerson, Person);
OldPerson.prototype.age = 0;
OldPerson.prototype.toString = function(){
    var oldString =  OldPerson.superclass.toString.call(this);
    return oldString + " and my age is " + this.age;
}

これは、少しひねりを加えたかなり一般的なパターンです。親クラスは「スーパークラス」プロパティを介して子にアタッチされ、子によってオーバーライドされたメソッド/プロパティにアクセスできます。技術的には、 に置き換えることができますがOldPerson.superclassPersonそれは理想的ではありません。Person 以外のクラスから継承するように OldPerson を変更した場合は、Person へのすべての参照も更新する必要があります。

編集 3: この一周を実現するために、Object.create を利用する「継承」関数のバージョンを次に示します。これは、以前に説明したものとまったく同じように機能します。

var inherits = function(childCtor, parentCtor) {
    childCtor.prototype = Object.create(parentCtor.prototype);
    childCtor.superclass = parentCtor.prototype; 
};
于 2012-04-28T09:16:44.640 に答える