Javascriptでのクラス作成

# はじめに

すでにいろんなところで既出ですが
割とよくできたので
JavaScriptでの疑似クラス作成例を備忘録として残しておきます
（ECMAScript6まではclassという概念はない）

privateメンバを実装するためにprototypeは使ってません
上記からパフォーマンスとか考えていません
とりあえずこうすればオブジェクト指向ライクにできるという実装例です

# 実装例

## 出来ること

- リフレクション（文字列指定）によるクラス生成
- private変数、private関数
- protected変数、protected関数
- 継承
- override
- 読み取り専用メンバ（const）



## ソースコード

```class.html
<script>

//-------------------------------
// ファクトリークラス（リフレクションによるクラスインスタンス生成）
Factory = function () {};
Factory.create = function (classname) {
    var args = [];
    for(var i=1; i < arguments.length; i++) args.push("arguments[%d]"); 
    return eval("new " + classname + "(" + args.join(",").replace(/%d/g, (function () {
        var i=1; return function () { return i++; };
	})()) + ")");
}

//-------------------------------

// 継承関数
var inherits = function inherits(sub, sup) {
    var F = function F () {};
    F.prototype = sup.prototype;
    sub.prototype = new F();
    sub.prototype.constructor = sub;
};

 
    
//-------------------------------

// 変数、関数を非公開にするため、クロージャにする
var Super = (function(){

	// クラス
	var Super = function Super(name,obj) {
    	// private変数を格納するオブジェクト
    	var pv = {};  
    	Object.defineProperties(pv, {
		  "name": {
		    value: name,
		    writable: true
		  },
		  "data": {
		    value: ["1","2"],
		    writable: true
		  }
		});
    	pv.PrivateFunc = PrivateFunc(pv);
    	pv.ProtectedFunc = ProtectedFunc(pv);
        
    	// public変数
    	Object.defineProperties(this, {
		  "obj": {
		    value: obj,
		    writable: false // readonly
		  } 
		});   
    	// publicメソッド
        this.SuperFunc = SuperFunc(pv);
        this.Interface = Interface(pv);
        
        // protectedメンバ
        return {
            data : pv.data,
            ProtectedFunc : pv.ProtectedFunc
        };

	};
	
	// private関数
	var PrivateFunc = function PrivateFunc(pv){
        return function() {
            console.log("Private func " + pv.name);        
        }
	}

    // protected関数
	var ProtectedFunc = function ProtectedFunc(pv){
        return function() {
            console.log("Protected func " + pv.name);        
        }
	}

	// private変数を引数にしているのがポイント
	var SuperFunc = function SuperFunc(pv) {
		// public用にクロージャで返す
        return function() {
            console.log(pv.name);
            pv.PrivateFunc(); // private関数呼び出し
        };
    };
    
	// インタフェース
    var Interface = function Interface(pv){
		// public用にクロージャで返す
	  	return function() {	  		
	  		console.log("Super " + pv.name);   
	  	} 
    };  	
	
	return Super;

})();



//-------------------------------

// 変数、関数を非公開にするため、クロージャにする
var Sub = (function(){

	// 子クラス
	var Sub = function Sub(name,obj,gender) {
        // 親コンストラクタの呼び出し(protectedメンバ取得)
    	var protect = Super.call(this, name,obj);
        console.log(protect);

		// private変数を格納するオブジェクト(protectedメンバも追加)
    	var pv = protect;  
    	Object.defineProperties(pv, {
		  "gender": {
		    value: gender,
		    writable: true
		  } 
		});
    	// publicメソッド		    	
    	this.SubFunc = SubFunc(pv);
        this.Interface = Interface(pv);
	};
	// 親クラスのpublicプロパティ・プロトタイプ継承
	inherits(Sub, Super);
	
	var SubFunc = function SubFunc(pv){
		// public用にクロージャで返す
	  	return function() {
			console.log("Sub func");
            pv.ProtectedFunc(); // protected関数呼び出し
	  	} 
    };  
	
	// インタフェース(override)
    var Interface = function Interface(pv){
		// public用にクロージャで返す
	  	return function() {	  		
	  		console.log("Sub " + pv.gender);   
	  	} 
    };  
	
	return Sub;
})();

//-------------------------------

// -- ユーザーコード --
var k1 = Factory.create("Sub","name1",{obj:"obj"},"male");
console.log(k1.name);  // undefined
console.log(k1.obj); 
k1.obj = {dummy:"dummy1"}; // readonly(ignore write)
console.log(k1.obj); 
k1.SuperFunc(); 
k1.SubFunc(); 
k1.Interface(); 


var k2 = Factory.create("Sub","name2",{obj:"obj2"},"female");
console.log(k2.name);  // undefined
console.log(k2.obj); 
k2.obj = {dummy:"dummy2"}; // readonly(ignore write)
console.log(k2.obj); 
k2.SuperFunc(); 
k2.SubFunc(); 
k2.Interface(); 


</script>
```

実行例

![スクリーンショット 2015-05-16 20.30.18.png](https://qiita-image-store.s3.amazonaws.com/0/55077/b751c294-6cba-80c3-cf56-4d8651ca0338.png)