57

Object.createECMAScript 5 で導入された新しいメソッドに頭を悩ませようとしてきました。

通常、継承を使用する場合は、次のようにします。

var Animal = function(name) { this.name = name; }
Animal.prototype.print = function() { console.log(this.name); }

var Dog = function() 
{ 
  return Animal.call(this, 'Dog'); 
}

Dog.prototype = new Animal();
Dog.prototype.bark = function() { console.log('bark'); }

新しく作成した Animal オブジェクトを Dog のプロトタイプに割り当てるだけで、すべてが魅力的に機能します。

var dog1 = new Dog();
dog1.print(); // prints 'Dog'
dog1.bark(); // prints 'bark'
dog1.name; //prints 'Dog'

しかし、人々は(説明せずに)Dog.prototype = new Animal();継承が機能する方法ではなく、Object.createアプローチを使用する必要があると言っています:

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

これも機能します。

使用する利点は何ですか、Object.createまたは何か不足していますか?

更新: それも機能すると言う人Dog.prototype = Animal.prototype;もいます。だから今、私は完全に混乱しています

4

4 に答える 4

125

Object.create以下では、継承を設定するのに が望ましい理由だけに関心があると仮定します。

その利点を理解するために、まず JavaScript で「クラス」が何で構成されているかを明確にしましょう。次の 2 つの部分があります。

  1. コンストラクター関数。この関数には、「クラス」のインスタンス、つまりインスタンス固有のコードを作成するためのすべてのロジックが含まれています。

  2. プロトタイプオブジェクト。これは、インスタンスが継承するオブジェクトです。これには、すべてのインスタンス間で共有する必要があるすべてのメソッド (およびその他のプロパティ) が含まれています。

継承は is-a関係を確立します。たとえば、 aDog anAnimalです。これは、コンストラクター関数とプロトタイプ オブジェクトの観点からどのように表現されますか?

明らかに、犬は動物と同じメソッドを持っている必要があります。つまり、Dog プロトタイプオブジェクトは、何らかの方法でAnimal プロトタイプオブジェクトのメソッドを組み込む必要があります。これを行うには複数の方法があります。あなたはしばしばこれを見るでしょう:

Dog.prototype = new Animal();

これが機能するのは、Animal インスタンスがAnimal プロトタイプオブジェクトから継承するためです。しかし、それはまた、すべての犬が1 つの特定のAnimal インスタンスから継承することも意味します。それは少し奇妙に思えます。インスタンス固有のコードは、コンストラクター関数でのみ実行するべきではありませんか? 突然、インスタンス固有のコードとプロトタイプメソッドが混在しているように見えます。

Animal その時点でインスタンス固有のコードを実際に実行する必要はありません。必要なのは、Animal プロトタイプオブジェクトのすべてのメソッドだけです。それがObject.create私たちができることです:

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

ここでは、新しいインスタンスを作成していませんAnimal。プロトタイプ メソッドのみを取得します。インスタンス固有のコードは、コンストラクター内の本来あるべき場所で正確に実行されます。

function Dog() { 
   Animal.call(this, 'Dog'); 
}

最大の利点は、常にObject.create機能することです。Usingは、コンストラクターが引数を想定していない場合にのみ機能します。コンストラクターが次のようになっていると想像してください。new Animal()

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

には常に文字列を渡すAnimal必要があります。そうしないと、エラーが発生します。あなたがするとき、あなたは何を渡しますDog.prototype = new Animal(??);か?何かを渡す限り、実際にはどの文字列を渡すかは問題ではありません。これは、これが悪い設計であることを示していることを願っています。


Dog.prototype = Animal.prototype;それもうまくいくと言う人もいます。だから今、私は完全に混乱しています

Animal.prototypeプロパティを からに「追加」するものはすべて「機能」しますDog.prototype。しかし、ソリューションの品質は異なります。この場合、 に追加するメソッドが にDog.prototypeも追加されるという問題が発生しますAnimal.prototype

例:

Dog.prototype.bark = function() {
    alert('bark');
};

以来Dog.prototype === Animal.prototype、すべてのAnimalインスタンスがメソッドを持つようbarkになりましたが、これは確かにあなたが望むものではありません。

Object.create(さらにはnew Animal)から継承する新しいオブジェクトを作成することにより、継承に1レベルの間接性を追加しAnimal.prototype、その新しいオブジェクトは になりDog.prototypeます。


ES6 での継承

ES6 では、コンストラクター関数とプロトタイプ メソッドを作成するための新しい構文が導入されています。これは次のようになります。

class Dog extends Animal {

  bark() {
    alert('bark');
  }

}

これは上で説明したものよりも便利ですが、結局のところ、継承をセットアップするためにextends内部で同等のものも使用しています。ES6 ドラフトObject.createの手順 2 と 3 を参照してください。 つまり、ES5 では using が「より正しい」アプローチです。
Object.create(SuperClass.prototype)

于 2013-06-30T17:48:42.953 に答える
9

まず、Animalコンストラクターを実行すると、望ましくない副作用が生じる可能性があります。このことを考慮:

var Animal = function(name) {
    this.name = name;
    Animal.instances.push(this);
};
Animal.instances = [];

このバージョンは、作成されたすべてのインスタンスを追跡します。あなたはDog.prototypeそこに記録されたくありません。

第二に、それは のメソッドになるDog.prototype = Animal.prototypeことを意味するので、悪い考えです。barkAnimal

于 2013-06-30T17:42:50.687 に答える
8

私は違いを少し説明しようとしています:

書くときに基本的に何が起こるかを次に示しますnew Animal()

    //creating a new object
    var res = {};

    //setting the internal [[prototype]] property to the prototype of Animal
    if (typeof Animal.prototype === "object" && Animal.prototype !== null) {
        res.__proto__ = Animal.prototype;
    }

    //calling Animal with the new created object as this
    var ret = Animal.apply(res, arguments);

    //returning the result of the Animal call if it is an object
    if (typeof ret === "object" && ret !== null) {
        return ret;
    }

    //otherise return the new created object
    return res;

そして、これが基本的に で起こることObject.createです:

    //creating a new object
    var res = {};

    //setting the internal [[prototype]] property to the prototype of Animal
    if (typeof Animal.prototype !== "object") {
        throw "....";
    }
    res.__proto__ = Animal.prototype;

    //return the new created object
    return res;

したがって、同じことを行いますが、関数を呼び出さず、Animal常に新しく作成されたオブジェクトを返します。あなたの場合、2 つの異なるオブジェクトが作成されます。最初の方法では、次のようになります。

Dog.prototype = {
    name: undefined,
    __proto__: Animal.prototype
};

2 番目の方法では次のようになります。

Dog.prototype = {
    __proto__: Animal.prototype
};

でインスタンスにname既に割り当てているため、プロトタイプにプロパティを含める必要はありません。DogAnimal.call(this, 'Dog');

主な目標は、インスタンスがプロトタイプDogのすべてのプロパティにアクセスできるようにすることです。これは、両方の方法で達成されます。Animalただし、最初の方法は、実際には必要のない余分な処理を実行するか、Pumbaa80 が述べたように望ましくない結果を引き起こす可能性さえあります。

于 2013-06-30T18:13:31.757 に答える