2007年11月27日火曜日

JavaScript と継承 3

Doublas Crockford さんがClassical Inheritance in JavaScript で JavaScript の継承についての記事を残していました.

Simple helper

まずは オブジェクトのプロトタイプに関数をバインドする簡単な関数です.これは以下に続く継承のヘルパとなっています.
// 新しい関数をオブジェクトのプロトタイプにバインドするヘルパ関数
Function.prototype.method = function(name, func){
this.prototype[name] = func;
return this;
}

継承

先ほどのヘルパ関数を使って継承関数を書きます.
Function.method('inherits', function(parent) {
// どのくらいの深さにいるか
var depth = 0;

// スーパークラスのメソッドを継承
var ptoro = this.prototype = new parent();

// 継承でオーバーライトされたスーパークラスのメソッドを呼出せるようにする
this.method('uber', function uber(name) {
var func;
var ret;
var v = parent.prototype;

// 既に uber 関数の中にいるとき
if (depth) {
for(var i = depth; i > 0; i++) {
v = v.constructer.prototype;
}

func = v[name];
}
else { // 最初の uber call
func = proto[name];
if (func == this[name]) {
func = v[name];
}
}

depth += 1;

ret = func.apply(this, Array.prototype.slice.apply(arguments, [1]));

depth -= 1;

return ret;

});
return this;
});

継承のポイント

上記の継承関数は Single parent の継承に使うことができます。コードのほとんどの部分は this.uber('methodName') を呼出すための部分で、これにより、オーバーライトした親メソッドを呼出すことができます。
オーバーライトしたメソッドのスーパークラスのメソッドを呼出しは、JavaScript の継承ではサポートされていないため、これで補えます。

使用例

上記の継承関数は以下のような形で使えます。
function Person(name) {
this.name = name;
}

Person.method('getName', function(){
return this.name
});

function User(name, password){
this.name = name;
this.password = password;
}

User.inherits(Person);

User.method('getPassword', function(){
return this.password;
});

// Overwrite したスーパークラスのメソッドを呼出し
User.method('getName', function(){
return 'My Name is:' this.uber('getName');
})