JavaScriptのクラスから継承する方法は?


99

PHP / Javaでは、次のことができます。

class Sub extends Base
{
}

また、スーパークラスのすべてのパブリック/保護されたメソッド、プロパティ、フィールドなどは、必要に応じてオーバーライドできるサブクラスの一部になります。

JavaScriptでこれに相当するものは何ですか?





そのクロックフォードの方法はまだ機能しますか?ZParenizor.inherits(Parenizor);
Loren Shqipognja

回答:


80

これを行う方法を変更しました。コンストラクター関数とそのprototypeプロパティを使用しないようにしていますが、2010からの私の古い答えはまだ一番下にあります。私は今好むObject.create()Object.createすべての最新のブラウザで利用できます。

Object.create通常、関数コンストラクタで使用するよりはるかに遅いことに注意してくださいnew

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

Object.createを使用する大きな利点の1つは、defineProperties引数を渡すことができることです。これにより、クラスのプロパティにアクセスして列挙する方法を大幅に制御できます。また、関数を使用してインスタンスを作成します。インスタンスを返すだけでなく、最後に初期化を行うことができるので、ある意味でコンストラクタ。

var Base = {};

function createBase() {
  return Object.create(Base, {
    doSomething: {
       value: function () {
         console.log("Doing something");
       },
    },
  });
}

var Sub = createBase();

function createSub() {
  return Object.create(Sub, {
    doSomethingElse: {
      value: function () {
        console.log("Doing something else");
      },
    },
  }); 
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

これは2010年の私の元の回答です。

function Base ( ) {
  this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
 console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"

5
sub.prototype.constructorの値はどうですか?サブバリューにも設定すべきだと思います。
最大値は

予約済みキーワード( 'super')をクラス名として使用している以外に、サンプルを実行できませんでした。 jsbin.com/ixiyet/8/edit
MOnsDaR

@MOnsDaRベース名を変更しました
ビョルン

私が使うなら alert()何をinstance.showColor()返すかを見るのても、私はまだ得undefinedます。jsbin.com/uqalin/1
MOnsDaR

1
@MOnsDaRは、コンソールがログに記録するため、アラートを表示するために何も返しません。showColorにreturnステートメントが表示されますか?
ビョルン

190

JavaScriptではクラスはありませんが、継承と動作の再利用をさまざまな方法で行うことができます。

疑似古典的継承(プロトタイピングによる):

function Super () {
  this.member1 = 'superMember1';
}
Super.prototype.member2 = 'superMember2';

function Sub() {
  this.member3 = 'subMember3';
  //...
}
Sub.prototype = new Super();

new演算子と一緒に使用する必要があります:

var subInstance = new Sub();

関数の適用または「コンストラクターの連鎖」:

function Super () {
  this.member1 = 'superMember1';
  this.member2 = 'superMember2';
}


function Sub() {
  Super.apply(this, arguments);
  this.member3 = 'subMember3';
}

このアプローチは、newオペレーターでも使用する必要があります。

var subInstance = new Sub();

最初の例との違いはapplySuperへのコンストラクタthisオブジェクトの内部にSub、それが割り当てられたプロパティ追加this上にSuper直接、新しいインスタンス上で、例えば、subInstanceプロパティが含まれていますmember1し、member2直接(subInstance.hasOwnProperty('member1') == true;)。

最初の例では、これらのプロパティはプロトタイプチェーンを通じて到達し、内部[[Prototype]]オブジェクトに存在します。

寄生継承またはパワーコンストラクター:

function createSuper() {
  var obj = {
    member1: 'superMember1',
    member2: 'superMember2'
  };

  return obj;
}

function createSub() {
  var obj = createSuper();
  obj.member3 = 'subMember3';
  return obj;
}

このアプローチは基本的に「オブジェクト拡張」に基づいています。使用する必要はありません new演算子thisはありません。ご覧のとおり、キーワードは関係していません。

var subInstance = createSub();

ECMAScript 5th Ed。Object.create方法:

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var superInstance = {
  member1: 'superMember1',
  member2: 'superMember2'
};

var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';

上記の方法は、クロックフォードによって提案されたプロトタイプの継承技術ですです。

オブジェクトインスタンスは他のオブジェクトインスタンスを継承します、それだけです。

以来、継承されたプロパティは、すべての新しいオブジェクト・インスタンスをコピーされないため、この技術は、より優れた単純な「オブジェクト増強」よりもでき、ベースオブジェクトは次のように設定されている[[Prototype]]拡張オブジェクト、上記の例ではsubInstance物理的のみを含むmember3プロパティ。


3
継承にインスタンスを使用しないでください- プロトタイプオブジェクトから直接継承するには、ES5 Object.create()またはカスタムclone()関数(例: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/clone.js)を使用してください。説明については、stackoverflow.com / questions / 1404559 /…へのコメントを参照してください
Christoph

@Christophに感謝し、私はこのObject.create方法について述べようとしていました:)
CMS

1
SubのプロトタイプにSuperのインスタンスメンバーが存在するため、これは適切な継承ではありません。したがって、Subのすべてのインスタンスは同じmember1変数を共有しますが、これはまったく望ましくありません。もちろん、書き換えることはできますが、それだけでは意味がありません。 github.com/dotnetwise/Javascript-FastClassは、より優れた砂糖ソリューションです。
Adaptabi 2014

@CMS様、最初の例で親クラスのインスタンスを作成して、サブクラスの継承をセットアップする必要があるのはなぜですか?私はこの行について話している:Sub.prototype = new Super();。スクリプトの実行中に両方のクラスが使用されない場合はどうなりますか?パフォーマンスの問題のようです。子クラスが実際に使用されていないのに、なぜ親クラスを作成する必要があるのですか?詳しく説明していただけますか?これが問題の簡単なデモです:jsfiddle.net/slavafomin/ZeVL2ありがとう!
Slava Fomin II

最後の例を除くすべての例には、Superの「クラス」とSubの「クラス」があり、Subのインスタンスを作成します。Object.createの例に相当する例を追加できますか?
ルーク

49

2019年以降にこのページにアクセスする方

ECMAScript標準の最新バージョン(ES6)では、次のキーワードを使用できます。class

クラス定義は通常のものではないことに注意してくださいobject。したがって、クラスメンバー間にコンマはありません。クラスのインスタンスを作成するには、newキーワードを使用する必要があります。基本クラスから継承するには、次を使用しますextends

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
var myVehicle = new Vehicle('rocky');
myVehicle.getName(); // => 'rocky'

基本クラスから継承するには、次を使用しますextends

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

var myCar = new Car('bumpy');

myCar.getName(); // => 'bumpy'
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true

派生クラスから、任意のコンストラクターまたはメソッドからのスーパーを使用して、その基本クラスにアクセスできます。

  • 親コンストラクタを呼び出すには、次を使用します super().
  • 別のメンバーを呼び出すには、たとえばを使用しますsuper.getName()

クラスの使い方には他にもあります。主題をさらに掘り下げたい場合は、「ECMAScript 6のクラス」をお勧めします詳しくアクセルラウシュマイヤー博士による「ます。*

ソース


1
ボンネットの下に、classそしてextendsある(超便利)プロトタイプチェーンの構文糖:stackoverflow.com/a/23877420/895245
チロSantilli郝海东冠状病六四事件法轮功

あなたの情報のためだけに 'instance.name'ここで 'mycar.name'はクラスの名前を返します。これはES6およびESnextのデフォルトの動作です。ここでmycar.nameは「Vehicle」を返します
Shiljo Paulson

7

JavaScriptでは、「クラスの継承」はなく、「プロトタイプの継承」だけがあります。したがって、クラスを「トラック」にして、「自動車」のサブクラスとしてマークすることはありません。代わりに、オブジェクトを「ジャック」にして、「ジョン」をプロトタイプとして使用するとします。ジョンが知っている場合、「4 + 4」の程度は、ジャックも知っています。

私はあなたがここに原型継承についてダグラス・クロックフォードの記事を読むことをお勧め:http://javascript.crockford.com/prototypal.html彼はまた、あなたはJavaScriptを、他のオブジェクト指向言語のように「そっくり」の継承を持って作ることができる方法を示しては、このことを説明して実際には、使用することを意図していない方法でjavaScriptを破壊することを意味します。


ジャックのプロトタイプがジョンだとしましょう。実行時に、ジョンにプロパティ/動作を追加しました。ジャックからそのプロパティ/動作を取得できますか?
Ram Bavireddi、2015年

あなたは確かにします。たとえば、これは通常、人々がすべての文字列オブジェクトに「trim()」メソッドを追加する方法です(組み込みではありません)。次の例を参照してください:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ …
ナイービスト、2015年

6

私はこの引用が最も啓発的であると思います:

本質的に、JavaScriptの「クラス」は、コンストラクターに加えて、接続されたプロトタイプオブジェクトとして機能する単なるFunctionオブジェクトです。(出典:Guru Katz

オブジェクトではなくコンストラクターを使用するのが好きなので、ここでCMSによって説明されている「疑似古典的継承」メソッドには不慣れです。以下に、プロトタイプチェーンを使用した多重継承の例を示します

// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
    this.isLifeform = true;
}

// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
    this.isAnimal = true;
}
Animal.prototype = new Lifeform();

// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
    this.isMammal = true;
}
Mammal.prototype = new Animal();

// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true

// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
    ,tiger.hasOwnProperty("isLifeform") // false
    ,tiger.hasOwnProperty("isAnimal")   // false
    ,tiger.hasOwnProperty("isMammal")   // false
    ,tiger.hasOwnProperty("isCat")      // true
);

// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A    = 1;
Animal.prototype.B      = 2;
Mammal.prototype.C      = 3;
Cat.prototype.D         = 4;

console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4

// Look at the instance object again
console.log(tiger);
// You'll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) );  // Mammal 
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform

ここでMDNから別の優れたリソースは、ここであなたはそれを試してみることができるようにjsfiddle


4

Javascriptの継承は、実際にはクラスがないため、JavaやPHPとは少し異なります。代わりに、メソッドとメンバー変数を提供するプロトタイプオブジェクトがあります。これらのプロトタイプをチェーンして、オブジェクトの継承を提供できます。この質問を調査したときに見つけた最も一般的なパターンは、Mozilla Developer Networkに記載されています。スーパークラスメソッドへの呼び出しを含め、アラートメッセージにログを表示するようにサンプルを更新しました。

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);

個人的には、Javascriptでの継承は厄介ですが、これは私が見つけた中で最も優れたバージョンです。


3

できません(古典的な意味で)。JavaScriptはプロトタイプ言語です。JavaScriptで「クラス」を宣言しないことがわかります。オブジェクトの状態とメソッドを定義するだけです。継承を生成するには、オブジェクトを取得してプロトタイプを作成します。プロトタイプは新しい機能で拡張されています。


1

あなたは使用することができます.inheritWithし、.fastClass ライブラリ。ほとんどの一般的なライブラリよりも高速で、ネイティブバージョンよりも高速な場合もあります。

非常に使いやすい:

function Super() {
   this.member1 = "superMember";//instance member
}.define({ //define methods on Super's prototype
   method1: function() { console.log('super'); } //prototype member
}.defineStatic({ //define static methods directly on Super function 
   staticMethod1: function() { console.log('static method on Super'); }
});

var Sub = Super.inheritWith(function(base, baseCtor) {
   return {
      constructor: function() {//the Sub constructor that will be returned to variable Sub
         this.member3 = 'subMember3'; //instance member on Sub
         baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
      },
      method1: function() { 
         console.log('sub'); 
         base.method1.apply(this, arguments); //call the base class' method1 function
      }
}

使用法

var s = new Sub();
s.method1(); //prints:
//sub 
//super

1
function Person(attr){
  this.name = (attr && attr.name)? attr.name : undefined;
  this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;

  this.printName = function(){
    console.log(this.name);
  }
  this.printBirthYear = function(){
    console.log(this.birthYear);
  }
  this.print = function(){
    console.log(this.name + '(' +this.birthYear+ ')');
  }
}

function PersonExt(attr){
  Person.call(this, attr);

  this.print = function(){
    console.log(this.name+ '-' + this.birthYear);
  }
  this.newPrint = function(){
    console.log('New method');
  }
}
PersonExt.prototype = new Person();

// Init object and call methods
var p = new Person({name: 'Mr. A', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A

var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A

1

多くの投稿を読んだ後、私はこの解決策を思いつきましたjsfiddle here)。ほとんどの場合、もっと洗練されたものは必要ありません

var Class = function(definition) {
    var base = definition.extend || null;
    var construct = definition.construct || definition.extend || function() {};

    var newClass = function() { 
        this._base_ = base;        
        construct.apply(this, arguments);
    }

    if (definition.name) 
        newClass._name_ = definition.name;

    if (definition.extend) {
        var f = function() {}       
        f.prototype = definition.extend.prototype;      
        newClass.prototype = new f();   
        newClass.prototype.constructor = newClass;
        newClass._extend_ = definition.extend;      
        newClass._base_ = definition.extend.prototype;         
    }

    if (definition.statics) 
        for (var n in definition.statics) newClass[n] = definition.statics[n];          

    if (definition.members) 
        for (var n in definition.members) newClass.prototype[n] = definition.members[n];    

    return newClass;
}


var Animal = Class({

    construct: function() {        
    },

    members: {

        speak: function() {
            console.log("nuf said");                        
        },

        isA: function() {        
            return "animal";           
        }        
    }
});


var Dog = Class({  extend: Animal,

    construct: function(name) {  
        this._base_();        
        this.name = name;
    },

    statics: {
        Home: "House",
        Food: "Meat",
        Speak: "Barks"
    },

    members: {
        name: "",

        speak: function() {
            console.log( "ouaf !");         
        },

        isA: function(advice) {
           return advice + " dog -> " + Dog._base_.isA.call(this);           
        }        
    }
});


var Yorkshire = Class({ extend: Dog,

    construct: function(name,gender) {
        this._base_(name);      
        this.gender = gender;
    },

    members: {
        speak: function() {
            console.log( "ouin !");           
        },

        isA: function(advice) {         
           return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);       
        }        
    }
});


var Bulldog = function() { return _class_ = Class({ extend: Dog,

    construct: function(name) {
        this._base_(name);      
    },

    members: {
        speak: function() {
            console.log( "OUAF !");           
        },

        isA: function(advice) {         
           return "bulldog -> " + _class_._base_.isA.call(this,advice);       
        }        
    }
})}();


var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();

var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();

var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();

var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("nice"));
bulldog.speak();

1

CMSの回答のおかげで、プロトタイプとObject.createをしばらくいじった後、applyを使用して、継承を適切に解決することができました。

var myNamespace = myNamespace || (function() {
    return {

        BaseClass: function(){
            this.someBaseProperty = "someBaseProperty";
            this.someProperty = "BaseClass";
            this.someFunc = null;
        },

        DerivedClass:function(someFunc){
            myNamespace.BaseClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "DerivedClass";
        },

        MoreDerivedClass:function(someFunc){
            myNamespace.DerivedClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "MoreDerivedClass";
        }
    };
})();


1
function Base() {
    this.doSomething = function () {
    }
}

function Sub() {
    Base.call(this); // inherit Base's method(s) to this instance of Sub
}

var sub = new Sub();
sub.doSomething();

2
コードを投稿するだけでなく、コードの機能と質問への回答を説明してください。
Patrick Hund 2017年

1

ES6クラス:

JavaScriptにはクラスがありません。JavaScriptのクラスは、JavaScriptのプロトタイプの継承パターンの上に構築された構文上の砂糖です。JS classを使用してプロトタイプの継承を適用できますが、実際には内部でコンストラクター関数を実際に使用していることを認識することが重要です。

これらの概念es6は、extendsキーワードを使用して「クラス」から拡張する場合にも適用されます。これにより、プロトタイプチェーンに追加のリンクが作成されます。の__proto__

例:

class Animal {
  makeSound () {
    console.log('animalSound');
  }
}

class Dog extends Animal {
   makeSound () {
    console.log('Woof');
  }
}


console.log(typeof Dog)  // classes in JS are just constructor functions under the hood

const dog = new Dog();

console.log(dog.__proto__ === Dog.prototype);   
// First link in the prototype chain is Dog.prototype

console.log(dog.__proto__.__proto__ === Animal.prototype);  
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal

Object.create()

Object.create()JavaScriptでJSに継承を作成する方法でもあります。Object.create()新しいオブジェクトを作成する関数です。既存のオブジェクトを引数として使用します。引数として受け取ったオブジェクトを__proto__、新しく作成されたオブジェクトのプロパティにます。ここでも、JSが具現化するプロトタイプの継承パラダイムに縛られていることを認識することが重要です。

例:

const Dog = {
  fluffy: true,
  bark: () => {
      console.log('woof im a relatively cute dog or something else??');
  }
};

const dog = Object.create(Dog);

dog.bark();


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.