JavaScriptでclassを継承する
JavaScriptでclassをシュミレートする方法
JavaScriptはprototypeベースのオブジェクト指向で、classという機能は現時点ではありません。
prototypeとは、「原型」という意味です。JavaScriptではあるオブジェクトが他のオブジェクトの原型となることで、オブジェクトの拡張・継承がなされます。一つの具体的な手順としては、オブジェクトのコンストラクタとして関数を利用し、その関数のprototypeプロパティに原型オブジェクトを設定します。
このような手順を取る場合には、関数がオブジェクトを作ったり、繋げたりする機能を果たすということになります。関数のprototypeプロパティには、どのようなオブジェクトでも設定することができますが、「原型」としてのみ利用するオブジェクトを設定することで、関数をclassに見立たてたclassベースのオブジェクト指向をシュミレートすることができます。
classに見立てた関数に、毎回その継承手順を行うのですが、これは継承設定の柔軟性の確保にはなりますが、多少手間がかかります。そこで、基本的な継承手順を自動化した関数を作ります。
継承手順には、大きくプロトタイプチェーン、コンストラクタチェーン、メソッドチェーンがありますが、プロトタイプチェーン関数だけでも作っておくと作業効率が上がります。
以下に、私が作った継承関数を紹介します。プロトタイプチェーンだけではなく、コンストラクタチェーン、メソッドチェーンも楽に作業できるように工夫しました。(メソッドチェーンは、親クラスへの参照があるというだけですが、、。)
関数名: inherit
引数: 親class、子classのコンストラクタ
返値: 子class
説明:
JavaScriptで、classに見立てた関数の継承作業を自動化する関数です。
第一引数に親class、第二引数に子classのコンストラクタを取り、返値として子classを返します。
子classのプロパティ.parentCtorに、親classのコンストラクタが定義されます。これをコンストラクタチェーンに使ってください。
子classのプロパティ.parentに、親classへの参照が定義されます。これをメソッドチェーンに使ってください。
仕様解説
特色
JavaScriptでは、関数がコンストラクタになるので、その関数をclassに見立てると解説しましたが、このclass継承関数inheritでは、classに見立てる関数とは別に、それとセットとなるコンストラクタ関数を用意しました。これによりclass継承作業の柔軟性を確保します。
コスト
子class生成時のinherit実行コールオブジェクト。
子classとセットになるコンストラクタ。上記コールオブジェクトに格納される。
実行フロー
inherit実行時:
子classの定義
プロトタイプチェーン
親classへの参照設定
子classを返す → inherit実行コールオブジェクト残る
子class実行時:
子classに親コンストラクタ(を実行する関数parentCtor、以下同じ)を設定
→ 子class実行コールオブジェクト残る
子classのコンストラクタを実行 → 親コンストラクタを実行
親コンストラクタを廃棄 → 子class実行コールオブジェクト廃棄
使用例:
//親class宣言
function class1(test1,test2){
this.test1 = test1;
this.test2 = test2;
}
//子class宣言
var class2 = inherit(class1,function(test1,test3){
//コンストラクタチェーン。引数test2をバインド。
class2.parentCtor(test1,10);
this.test3 = test3;
});
関数のコード:
function inherit(parent,ctor){
//子classを定義する。
function child(){
if(ctor){
//コンストラクタチェーンをchildにセットする。
var itself = this;
child.parentCtor = function(){
parent.apply(itself,arguments);
}
//コンストラクタを実行する。
ctor.apply(this,arguments);
//実行済みの親コンストラクタを廃棄する。
delete child.parentCtor;
}
}
//プロトタイプチェーン。
function temClass(){} //プロトタイプのみ継承するため。
temClass.prototype = parent.prototype;
child.prototype = new temClass();
//constructorプロパティを修正する
child.prototype.constructor = child;
//メソッドチェーンのため、親classへの参照を格納。
child.parent = parent;
return child;
};
動作確認用コード:
以下のコードは、jsdo.itで試行できます。
親class1、子class2、孫class3
//class関数の定義
var Utility = {};
Utility.inherit = function(parent,ctor){
function child(){
if(ctor){
var itself = this;
child.parentCtor = function(){
parent.apply(itself,arguments);
}
ctor.apply(this,arguments);
delete child.parentCtor;
}
}
function temClass(){}
temClass.prototype = parent.prototype;
child.prototype = new temClass();
child.prototype.constructor = child;
child.parent = parent;
return child;
};
//親class1の定義
function class1(test1,test2){
this.test1 = test1;
this.test2 = test2;
}
//親class1のメソッド定義
class1.prototype.display1 = function(){
alert(this.test1);
};
class1.prototype.display2 = function(){
alert(this.test2);
};
//子class2の定義
var class2 = Utility.inherit(class1,function(test1,test3){
//コンストラクタチェーン。引数test2をバインド。
class2.parentCtor(test1,10);
this.test3 = test3;
});
//子class2のメソッド定義
class2.prototype.display3 = function(){
alert(this.test3);
};
//孫class3の定義
var class3 = Utility.inherit(class2,function(test1){
//コンストラクタチェーン。引数test3をバインド。
class3.parentCtor(test1,5);
});
//子class2のオブジェクト生成
var class2Obj = new class2(1,2);
//テスト、メソッド実行
class2Obj.display1(); //1
class2Obj.display2(); //10
class2Obj.display3(); //2
//孫class3のオブジェクト生成
var class3Obj = new class3(1);
var class3Obj2 = new class3(2);
//テスト、メソッド実行、class3Obj
class3Obj.display1(); //1
class3Obj.display2(); //10
class3Obj.display3(); //5
//テスト、メソッド実行、class3Obj2
class3Obj2.display1(); //2
class3Obj2.display2(); //10
class3Obj2.display3(); //5
参考文献:
JavaScript 第5版, David Flanagan, 訳:村上 列, 株式会社オライリー・ジャパン, 2009年3月16日
クラスの継承の仕方 – JavaScript – 教えて!goo, http://oshiete.goo.ne.jp/qa/1617352.html, 2005年8月31日


