基本クラスがあります:
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でこれを達成する最も簡単で信頼性の高い方法は何でしょうか?
ES6について以下に更新
この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()
がサポートされていないことに注意してください。
これらをサポートする必要がある場合、リンクされたMDNドキュメントは、ポリフィルまたは次の近似を使用することを提案しています。
function createObject(proto) {
function ctor() { }
ctor.prototype = proto;
return new ctor();
}
これをStudent.prototype = createObject(Person.prototype)
のように使用することは、new Person()
を使用するよりも望ましい 親のコンストラクター関数の呼び出しを回避する プロトタイプを継承し、継承者のコンストラクターが呼び出されるときにのみ親コンストラクターを呼び出す.
ありがたいことに、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では、class&extendsキーワードを使用する機会が得られました。
次に、コードは次のようになります。
基本クラスがあります:
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
}
}
これを試して:
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"
プロトタイプアプローチが気に入らない場合は、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
これは、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つのバリアントを提案することができます、それは最も簡単なようです:
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
従来の拡張の場合、単にコンストラクター関数としてスーパークラスを記述し、継承したクラスにこのコンストラクターを適用できます。
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');
}
}])
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
}
}
Javascriptのプロトタイプを使用してコンストラクター関数を拡張する問題を解決できる方法は複数あります。これらの方法のどれが「最良の」ソリューションであるかは意見に基づいています。ただし、コンストラクターの関数プロトタイプを拡張するために頻繁に使用される2つのメソッドがあります。
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
を評価する最初のログは、これが関数であることを観察できます。これは、クラスが内部の単なるコンストラクタ関数であるためです。それにもかかわらず、プロトタイプ継承を実装するこの方法を好むかもしれませんし、間違いなくそれを学ぶべきです。 ReactJS
やAngular2+
などの主要なフレームワークで使用されます。
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を割り当てます。
絶対に最小限の(上記の回答の多くとは異なり、正しい)バージョンは次のとおりです。
function Monkey(param){
this.someProperty = param;
}
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana){ banana.eat() }
それで全部です。 ここで長い説明 を読むことができます