web-dev-qa-db-ja.com

JavaScript拡張クラス

基本クラスがあります:

function Monster() {
  this.health = 100;
}

Monster.prototype.growl = function() {
  console.log("Grr!");
}

私が拡張して別のクラスを作成したいこと:

function Monkey extends Monster() {
  this.bananaCount = 5;
}

Monkey.prototype.eatBanana {
  this.bananaCount--;
  this.health++; //Accessing variable from parent class monster
  this.growl();  //Accessing function from parent class monster
}

私はかなりの研究を行ってきましたが、JavaScriptでこれを行うための多くの複雑なソリューションがあるようです。 JSでこれを達成する最も簡単で信頼性の高い方法は何でしょうか?

69
Lucas Penney

ES6について以下に更新

2013年3月およびES5

このMDNドキュメントでは、クラスの拡張について詳しく説明しています。

https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript

特に、ここで彼らはそれを処理します:

// define the Person Class
function Person() {}

Person.prototype.walk = function(){
  alert ('I am walking!');
};
Person.prototype.sayHello = function(){
  alert ('hello');
};

// define the Student class
function Student() {
  // Call the parent constructor
  Person.call(this);
}

// inherit Person
Student.prototype = Object.create(Person.prototype);

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;

// replace the sayHello method
Student.prototype.sayHello = function(){
  alert('hi, I am a student');
}

// add sayGoodBye method
Student.prototype.sayGoodBye = function(){
  alert('goodBye');
}

var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();

// check inheritance
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true

IE8を含む一部の古いブラウザでは、 Object.create() がサポートされていないことに注意してください。

Object.create browser support

これらをサポートする必要がある場合、リンクされたMDNドキュメントは、ポリフィルまたは次の近似を使用することを提案しています。

function createObject(proto) {
    function ctor() { }
    ctor.prototype = proto;
    return new ctor();
}

これをStudent.prototype = createObject(Person.prototype)のように使用することは、new Person()を使用するよりも望ましい 親のコンストラクター関数の呼び出しを回避する プロトタイプを継承し、継承者のコンストラクターが呼び出されるときにのみ親コンストラクターを呼び出す.

2017年5月およびES6

ありがたいことに、JavaScriptの設計者は私たちの助けを求める声を聞いており、この問題に取り組むためのより適切な方法を採用しています。

MDN にはES6クラスの継承に関する別の優れた例がありますが、ES6で再現された上記とまったく同じクラスのセットを示します。

class Person {
    sayHello() {
        alert('hello');
    }

    walk() {
        alert('I am walking!');
    }
}

class Student extends Person {
    sayGoodBye() {
        alert('goodBye');
    }

    sayHello() {
        alert('hi, I am a student');
    }
}

var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();

// check inheritance
alert(student1 instanceof Person); // true 
alert(student1 instanceof Student); // true

誰もが望むように、清潔で理解しやすい。 ES6は非常に一般的ですが、 どこでもサポートされていない

ES6 browser support

135
Oliver Spryn

ES6では、classextendsキーワードを使用する機会が得られました。

次に、コードは次のようになります。

基本クラスがあります:

class Monster{
       constructor(){
             this.health = 100;
        }
       growl() {
           console.log("Grr!");
       }

}

以下を使用して、別のクラスを拡張および作成します。

class Monkey extends Monster {
        constructor(){
            super(); //don't forget "super"
            this.bananaCount = 5;
        }


        eatBanana() {
           this.bananaCount--;
           this.health++; //Accessing variable from parent class monster
           this.growl(); //Accessing function from parent class monster
        }

}
13
Abdennour TOUMI

これを試して:

Function.prototype.extends = function(parent) {
  this.prototype = Object.create(parent.prototype);
};

Monkey.extends(Monster);
function Monkey() {
  Monster.apply(this, arguments); // call super
}

編集:ここに簡単なデモを置きます http://jsbin.com/anekew/1/editextendsはJSの予約語であり、コードをリントするときに警告が表示される場合があります。単純にinheritsという名前を付けることができます。

このヘルパーを配置し、オブジェクトpropsを唯一のパラメーターとして使用すると、JSでの継承が少し簡単になります。

Function.prototype.inherits = function(parent) {
  this.prototype = Object.create(parent.prototype);
};

function Monster(props) {
  this.health = props.health || 100;
}

Monster.prototype = {
  growl: function() {
    return 'Grrrrr';
  }
};

Monkey.inherits(Monster);
function Monkey() {
  Monster.apply(this, arguments);
}

var monkey = new Monkey({ health: 200 });

console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"
10
elclanrs

プロトタイプアプローチが気に入らない場合は、Nice OOPウェイでは実際に動作しないため、これを試すことができます。

var BaseClass = function() 
{
    this.some_var = "foobar";

    /**
     * @return string
     */
    this.someMethod = function() {
        return this.some_var;
    }
};

var MyClass = new Class({ extends: BaseClass }, function()
{
    /**
     * @param string value
     */
    this.__construct = function(value)
    {
        this.some_var = value;
    }
})

軽量ライブラリの使用(2k縮小): https://github.com/haroldiedema/joii

6
Harold

これは、elclanrsのソリューションの拡張(言い訳)であり、インスタンスメソッドの詳細を含めるとともに、質問のその側面に拡張可能なアプローチを採用しています。これは、David Flanaganの「JavaScript:The Definitive Guide」(このコンテキストに合わせて部分的に調整)のおかげでまとめられていることを完全に認めます。これは明らかに他のソリューションよりも冗長ですが、おそらく長期的には有益であることに注意してください。

最初に、Davidの単純な「extend」関数を使用します。この関数は、指定されたオブジェクトにプロパティをコピーします。

function extend(o,p) {
    for (var prop in p) {
        o[prop] = p[prop];
    }
    return o;
}

次に、サブクラス定義ユーティリティを実装します。

function defineSubclass(superclass,     // Constructor of our superclass
                          constructor,  // Constructor of our new subclass
                          methods,      // Instance methods
                          statics) {    // Class properties
        // Set up the prototype object of the subclass
    constructor.prototype = Object.create(superclass.prototype);
    constructor.prototype.constructor = constructor;
    if (methods) extend(constructor.prototype, methods);
    if (statics) extend(constructor, statics);
    return constructor;
}

最後の準備として、Davidの新しいジグリーポケリーで関数プロトタイプを強化します。

Function.prototype.extend = function(constructor, methods, statics) {
    return defineSubclass(this, constructor, methods, statics);
};

Monsterクラスを定義した後、次の操作を実行します(拡張/継承する新しいクラスで再利用可能です)。

var Monkey = Monster.extend(
        // constructor
    function Monkey() {
        this.bananaCount = 5;
        Monster.apply(this, arguments);    // Superclass()
    },
        // methods added to prototype
    {
        eatBanana: function () {
            this.bananaCount--;
            this.health++;
            this.growl();
        }
    }
);
1
GMeister

私は本を​​読んだだけで、1つのバリアントを提案することができます、それは最も簡単なようです:

function Parent() { 
  this.name = 'default name';
};

function Child() {
  this.address = '11 street';
};

Child.prototype = new Parent();      // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment

var a = new Child(); 

console.log(a.name);                // "default name" trying to reach property of inherited class
1
Yarik

従来の拡張の場合、単にコンストラクター関数としてスーパークラスを記述し、継承したクラスにこのコンストラクターを適用できます。

     function AbstractClass() {
      this.superclass_method = function(message) {
          // do something
        };
     }

     function Child() {
         AbstractClass.apply(this);
         // Now Child will have superclass_method()
     }

Angularjsの例:

http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview

app.service('noisyThing', 
  ['notify',function(notify){
    this._constructor = function() {
      this.scream = function(message) {
          message = message + " by " + this.get_mouth();
          notify(message); 
          console.log(message);
        };

      this.get_mouth = function(){
        return 'abstract mouth';
      }
    }
  }])
  .service('cat',
  ['noisyThing', function(noisyThing){
    noisyThing._constructor.apply(this)
    this.meow = function() {
      this.scream('meooooow');
    }
    this.get_mouth = function(){
      return 'fluffy mouth';
    }
  }])
  .service('bird',
  ['noisyThing', function(noisyThing){
    noisyThing._constructor.apply(this)
    this.twit = function() {
      this.scream('fuuuuuuck');
    }
  }])
0

Autodidactsの場合:

function BaseClass(toBePrivate){
    var morePrivates;
    this.isNotPrivate = 'I know';
    // add your stuff
}
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';


// MiddleClass extends BaseClass
function MiddleClass(toBePrivate){
    BaseClass.call(this);
    // add your stuff
    var morePrivates;
    this.isNotPrivate = 'I know';
}
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';



// TopClass extends MiddleClass
function TopClass(toBePrivate){
    MiddleClass.call(this);
    // add your stuff
    var morePrivates;
    this.isNotPrivate = 'I know';
}
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';


// to be continued...

ゲッターとセッターで「インスタンス」を作成します。

function doNotExtendMe(toBePrivate){
    var morePrivates;
    return {
        // add getters, setters and any stuff you want
    }
}
0
Steffomio

概要:

Javascriptのプロトタイプを使用してコンストラクター関数を拡張する問題を解決できる方法は複数あります。これらの方法のどれが「最良の」ソリューションであるかは意見に基づいています。ただし、コンストラクターの関数プロトタイプを拡張するために頻繁に使用される2つのメソッドがあります。

ES 2015クラス:

class Monster {
  constructor(health) {
    this.health = health
  }
  
  growl () {
  console.log("Grr!");
  }
  
}


class Monkey extends Monster {
  constructor (health) {
    super(health) // call super to execute the constructor function of Monster 
    this.bananaCount = 5;
  }
}

const monkey = new Monkey(50);

console.log(typeof Monster);
console.log(monkey);

ES 2015クラスを使用する上記のアプローチは、javascriptのプロトタイプ継承パターンに対する構文上の砂糖にすぎません。ここでtypeof Monsterを評価する最初のログは、これが関数であることを観察できます。これは、クラスが内部の単なるコンストラクタ関数であるためです。それにもかかわらず、プロトタイプ継承を実装するこの方法を好むかもしれませんし、間違いなくそれを学ぶべきです。 ReactJSAngular2+などの主要なフレームワークで使用されます。

Object.create()を使用するファクトリ関数:

function makeMonkey (bananaCount) {
  
  // here we define the prototype
  const Monster = {
  health: 100,
  growl: function() {
  console.log("Grr!");}
  }
  
  const monkey = Object.create(Monster);
  monkey.bananaCount = bananaCount;

  return monkey;
}


const chimp = makeMonkey(30);

chimp.growl();
console.log(chimp.bananaCount);

このメソッドはObject.create()メソッドを使用します。このメソッドは、新しく作成されたオブジェクトのプロトタイプとなるオブジェクトを返します。そのため、最初にこの関数でプロトタイプオブジェクトを作成し、Object.create()を呼び出して、__proto__プロパティがMonsterオブジェクトに設定された空のオブジェクトを返します。この後、オブジェクトのすべてのプロパティを初期化できます。この例では、新しく作成されたオブジェクトにbananacountを割り当てます。

0

絶対に最小限の(上記の回答の多くとは異なり、正しい)バージョンは次のとおりです。

function Monkey(param){
  this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }

それで全部です。 ここで長い説明 を読むことができます

0
gdanov