web-dev-qa-db-ja.com

Javascriptの継承:スーパーコンストラクターを呼び出すか、プロトタイプチェーンを使用しますか?

ごく最近、MDCでのJavaScript呼び出しの使用について読みました

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

以下に示す例の1つのリンク、私はまだ理解していません。

なぜ彼らはここでこのように継承を使用していますか

Prod_dept.prototype = new Product();

これは必要ですか?でスーパーコンストラクターへの呼び出しがあるため

Prod_dept()

とにかく、このように

Product.call

これはただの一般的な行動から外れているのでしょうか?スーパーコンストラクターの呼び出しを使用するか、プロトタイプチェーンを使用する方が良いのはいつですか?

function Product(name, value){
  this.name = name;
  if(value >= 1000)
    this.value = 999;
  else
    this.value = value;
}

function Prod_dept(name, value, dept){
  this.dept = dept;
  Product.call(this, name, value);
}

Prod_dept.prototype = new Product();

// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");

// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");

物事を明確にしてくれてありがとう

77
Jeremy S.

本当の質問に対する答えは、両方を行う必要があるということです。

  • プロトタイプを親のインスタンスに設定すると、プロトタイプチェーンが初期化されます(継承)。これは1回だけ行われます(プロトタイプオブジェクトが共有されているため)。
  • 親のコンストラクターを呼び出すと、オブジェクト自体が初期化されます。これは、すべてのインスタンス化で行われます(構築するたびに異なるパラメーターを渡すことができます)。

したがって、継承を設定するときに親のコンストラクターを呼び出さないでください。別のオブジェクトを継承するオブジェクトをインスタンス化する場合のみ。

クリス・モーガンの答えはほとんど完全で、小さな詳細(コンストラクターのプロパティ)が欠けています。継承を設定する方法を提案します。

function extend(base, sub) {
  // Avoid instantiating the base class just to setup inheritance
  // Also, do a recursive merge of two prototypes, so we don't overwrite 
  // the existing prototype, but still maintain the inheritance chain
  // Thanks to @ccnokes
  var origProto = sub.prototype;
  sub.prototype = Object.create(base.prototype);
  for (var key in origProto)  {
     sub.prototype[key] = origProto[key];
  }
  // The constructor property was set wrong, let's fix it
  Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
  });
}

// Let's try this
function Animal(name) {
  this.name = name;
}

Animal.prototype = {
  sayMyName: function() {
    console.log(this.getWordsToSay() + " " + this.name);
  },
  getWordsToSay: function() {
    // Abstract
  }
}

function Dog(name) {
  // Call the parent's constructor
  Animal.call(this, name);
}

Dog.prototype = {
    getWordsToSay: function(){
      return "Ruff Ruff";
    }
}    

// Setup the prototype chain the right way
extend(Animal, Dog);

// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog

クラスを作成する際の構文糖衣については、私のブログ投稿を参照してください。 http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Ext-JSおよび http://www.uselesspickles.com/class_library/ からコピーされたテクニックと https://stackoverflow.com/users/1397311/ccnokes からのコメント

106
Juan Mendes

理想的な方法は、not do Prod_dept.prototype = new Product();です。これは、Productコンストラクターを呼び出すためです。したがって、理想的な方法は、コンストラクターを除いて、次のようなものを複製することです。

function Product(...) {
    ...
}
var tmp = function(){};
tmp.prototype = Product.prototype;

function Prod_dept(...) {
    Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;

次に、構築時にスーパーコンストラクターが呼び出されます。これは、パラメーターを渡すこともできるためです。

Google Closure Libraryのようなものを見ると、それがその方法であることがわかります。

30
Chris Morgan

JavaScriptでオブジェクト指向プログラミングを行った場合、次のようにクラスを作成できることがわかります。

Person = function(id, name, age){
    this.id = id;
    this.name = name;
    this.age = age;
    alert('A new person has been accepted');
}

これまでのところ、クラスの担当者には2つのプロパティしかなく、いくつかのメソッドを指定します。これを行うクリーンな方法は、「プロトタイプ」オブジェクトを使用することです。 JavaScript 1.1から、プロトタイプオブジェクトがJavaScriptに導入されました。これは、オブジェクトのすべてのインスタンスにカスタムプロパティとメソッドを追加するプロセスを簡素化する組み込みオブジェクトです。次のように 'prototype'オブジェクトを使用して、クラスに2つのメソッドを追加しましょう。

Person.prototype = {
    /** wake person up */
    wake_up: function() {
        alert('I am awake');
    },

    /** retrieve person's age */
    get_age: function() {
        return this.age;
    }
}

クラスPersonを定義しました。 Personからいくつかのプロパティを継承するManagerと呼ばれる別のクラスを定義したい場合はどうでしょう。 Managerクラスを定義するときにこのプロパティをすべて再定義する必要はありません。クラスPersonから継承するように設定するだけです。 JavaScriptには継承が組み込まれていませんが、次のような手法を使用して継承を実装できます。

Inheritance_Manager = {}; //継承マネージャクラスを作成します(名前は任意です)

では、継承クラスに、baseClassおよびsubClassas引数を取るextendというメソッドを与えましょう。 extendメソッド内で、継承関数inheritance(){}と呼ばれる内部クラスを作成します。この内部クラスを使用する理由は、baseClassプロトタイプとsubClassプロトタイプの混乱を避けるためです。次に、次のコードのように、継承クラスのプロトタイプがbaseClassプロトタイプを指すようにします:inheritance.prototype = baseClass。プロトタイプ;次に、継承プロトタイプを次のようにsubClassプロトタイプにコピーします。subClass.prototype = new inheritance();次に、subClassのコンストラクターを次のように指定します。subClass.prototype.constructor = subClass;サブクラスのプロトタイピングが完了したら、次の2行のコードを指定して、いくつかの基本クラスポインターを設定できます。

subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;

拡張機能の完全なコードは次のとおりです。

Inheritance_Manager.extend = function(subClass, baseClass) {
    function inheritance() { }
    inheritance.prototype = baseClass.prototype;
    subClass.prototype = new inheritance();
    subClass.prototype.constructor = subClass;
    subClass.baseConstructor = baseClass;
    subClass.superClass = baseClass.prototype;
}

継承を実装したので、これを使用してクラスを拡張できます。この場合、次のようにPersonクラスをManagerクラスに拡張します。

Managerクラスを定義します

Manager = function(id, name, age, salary) {
    Person.baseConstructor.call(this, id, name, age);
    this.salary = salary;
    alert('A manager has been registered.');
}

personから継承します

Inheritance_Manager.extend(Manager, Person);

お気づきの方は、Inheritance_Managerクラスのextendメソッドを呼び出して、このケースではsubClass Managerを、次にbaseClass Personを渡しました。ここで順序が非常に重要であることに注意してください。それらをスワップすると、継承は意図したとおりに機能しません。また、サブクラスを実際に定義する前に、この継承を指定する必要があることに注意してください。次に、サブクラスを定義します。

以下のメソッドを追加できます。 Managerクラスは、Personクラスを継承するため、常にPersonクラスで定義されたメソッドとプロパティを持ちます。

Manager.prototype.lead = function(){
   alert('I am a good leader');
}

それをテストするために、2つのオブジェクトを作成します。1つはクラスPersonから、もう1つは継承クラスManagerからです:

var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');

次のURLで完全なコードとコメントを入手してください。 http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

6
Joe Francis