多段継承できるクラス生成関数を頑張って作りました

JavaScriptでクラスを作るのは簡単なのですが、継承したり、オーバーライドしたメソッドを簡単に呼び出すとなると途端にレベルが上がります。

  • ChromeFirefoxで使うので、関数呼び出し多めでも大丈夫(IE6でも動くけど)
  • this.parent.method()で親クラスのメソッド、this.parent.parent.method()で祖父クラスのメソッドが呼べる
    • this.parent.variableで親クラスのインスタンス変数とかはついていないです

という感じで、頑張って作ってみた。

使い方

var A = $_klass({
  init: function () { print("A init"); }, // コンストラクタ
  hoge: function () { print("A hoge"); },
  huga: function () { print("A huga"); },
  piyo: function () { print("A piyo"); }
});

var B = $_klass(A, {
//  init: function () { print("B init"); }, // コンストラクタは省略した時、親クラスを呼ぶ
  hoge: function () { print("B hoge"); },
//  huga: function () { print("B huga"); },
//  piyo: function () { print("B piyo"); }
});

var C = $_klass(B, {
  init: function () { print("C init"); },
  hoge: function () { print("C hoge"); },
//  huga: function () { print("C huga"); },
  piyo: function () { print("C piyo"); this.parent.piyo(); }
});

// インスタンスの生成
var a = new A; // A init
var b = new B; // A init
var c = new C; // C init

c.hoge(); // C hoge
c.huga(); // A huga
c.piyo(); // C piyo A piyo

ソース

function $_klass(parent, methods) {
	if (typeof parent === "object") {
		methods = parent;
		parent = null;
	}

	function klass() {
		this.klass = klass;
		if (klass.parent) {
			this.parent = makeParent(this, klass.parent);
		}
		this.init && this.init.apply(this, arguments);
	}
	klass.parent = parent;

	var kp = klass.fn = klass.prototype;

	if (parent) {
		var pp = parent.prototype;
		for (var i in pp) {
			var f = kp[i] = pp[i];
			if (typeof(f) === "function" && f.override) {
				kp[i] = (function (name) {
					return function f() {
						return this.parent(name, arguments);
					};
				})(i)
			}
		}
	}

	if (methods) for (var i in methods) {
		var t = kp[i], f = kp[i] = methods[i];
		if (typeof(f) === "function" && typeof(t) === "function") {
			f.override = true;
		}
	}

	return klass;

	function makeParent(self, parent) {
		var pp = parent.prototype, sp, tp;
		if (tp = parent.parent) sp = makeParent(self, tp);
		var fn = function (name, args) {
			var bk = self.parent;
			self.parent = sp;
			var rv = pp[name].apply(self, args || []);
			self.parent = bk;
			return rv;
		};
		for (var i in pp) if (typeof(pp[i]) === "function") {
			fn[i] = (function (name) {
				return function () { return self.parent(name, arguments); };
			})(i);
		}
		return fn;
	}
}

まとめ

  • for文でぶん回すわ、thisを束縛して関数作り直すわで、相当汚いので、誰か助けてください。
    • どうしてこうなった。