JavaScriptにはclassの概念がありませんでした。オブジェクト指向はprototypeを使ってそれらしいことを行なっているのですが、他言語をマスターした人にとっては奇々怪界なものです。
しかしながらES6ではclassの仕組みが用意されました。とはいえ根本的に他の言語のようなclassの仕組みではなく、かつてのprototypeベースの仕組みをclassを使って表現したものです。
したがってprototypeベースのオブジェクト指向の仕組みは理解しておく必要があります。
まずは簡単にprototypeベースの手法を確認しておきます。
prototypeベースのオブジェクト指向
次のコードは、prototypeベースのカスタムオブジェクトの記述例です。
function Car(options){ this.name = options.name; } Car.prototype.drive = function(){ return '愛するポンコツカー ' + this.name; } const car = new Car({name:'Subaru 360'}); console.log(car.name); console.log(car.drive());
この例の「function Car(){ }」は特別な関数でコンストラクタといいます。コンストラクタは別の関数と区別するために、頭文字を大文字にして名前を付けます。
コンストラクタはnew演算子でインスタンス化するときに呼び出されます。
コンストラクタ関数でのプロパティ定義
「this.name = options.name;」で使われているthisはnew演算子でインスタンス化してできるインスタンスを表します。このようにthisに繋げて変数を作ることで、これはインスタンスのプロパティとなります。
尚、コンストラクタ関数には戻り値は設定しません。
コンストラクタ関数とメソッドの定義そしてプロトタイプについて
コンストラクタ内でメソッドを定義することもできます。けれどもコンストラクタで無駄にメソッドを定義することは非効率とされています。
それはインスタンス化後に必ずしもそのメソッドを使うとは限らないからです。
JavaScriptの場合はメソッドを必要なときにいつでも追加できるからです。
そして一般的にメソッドをprototypeプロパティを使用して追加します。
記述は上の例のように「Car.prototype.drive = function(){ }」として追加します。
この手法がいわゆる「prototypeベースのオブジェクト指向」と呼ばれる所以です。
prototype.変数名 = function(){….}
prototypeベースのオブジェクト指向の継承
オブジェクト指向の継承とは、あるオブジェクトを作成しておいて、そのオブジェクトの機能を継承しながら新たなオブジェクトを作ることができます。
継承元のクラスをスーパークラスと呼び、継承されたクラスをサブクラスと言います。
JavaScriptのオブジェクト継承はプロトタイプチェーンの仕組みが使われています。
プロトタイプチェーンのポイントは継承したいオブジェクトを、継承する側のプロトタイプでインスタンス化することです。このことで継承が実行されます。
次の例では「Newcar.prototype = new Car({name:’Crown’})」で行っています。
継承のコード
//スーパークラス function Car(option){ this.name = option.name; } Car.prototype.drive = function(){ return '愛車 ' + this.name; } //インスタンス化 const car = new Car({name:'Subaru 360'}); console.log(car.name); console.log(car.drive()); //サブクラス function Newcar(option){ this.color = option.color; } Newcar.prototype = new Car({name:'Crown'}) Newcar.prototype.honk = function(){ return 'PuPuu!' } //サブクラスインスタンス化 const newcar = new Newcar({color:'white'}); console.log(newcar.color); console.log(newcar.drive()); console.log(newcar.honk());
尚、継承のしくみは Object.create() を使って行う方法もあります。
継承とプロトタイプチェーンの詳細はMDNのページを参考にしてください。
classを使った記述
ES6からclassの機能が使えるようになりました。
classはclassキーワードで定義します。また、class名は頭文字を大文字にします。
インスタンス化はnew演算子で行います。
コンストラクタは「 constructor( ){….}」のように宣言します。
メンバ変数はthisのあとに続けて記述します。これは旧来のコンストラクタ関数の書き方と同様です。
classブロック内でコンストラクタとは別に関数を定義するとメソッドを宣言したことになります。メソッドから他のメソッドやプロパティを呼び出す場合はthisを介して行います。
メソッド宣言時にstaticキーワードを付けると静的なメソッドとなります。
静的メソッドは、インスタンスを作らず呼び出せるメソッドです。逆にインスタンス経由では呼び出せませんので注意してください。
classの継承
classの継承は「class Newcar extends Car」のように「extend」キーワードを使います。
継承元のclassのコンストラクタにアクセスするには、「super(options)」のように継承先のコンストラクタ内で「super」を使います。
クラスベースのオブジェクトサンプルコード
//スーパークラス class Car { constructor({name}){ this.name = name; } drive(){ return '愛車 ' + this.name; } } //サブクラス class Newcar extends Car { constructor(options){ super(options); this.color = options.color; } honk(){ return 'pupu'; } } const car = new Car({name:'Subaru 360'}); console.log(car.name); console.log(car.drive()); const newcar = new Newcar({color:'red',name:'Subaru 360'}); console.log(newcar.name); console.log(newcar.color); console.log(newcar.honk());