ES5なJavascriptでモダンなクラス的継承＆スーパー呼び出し

# 動機 #

AltJSな言語ではなく、素のES5なJavascriptでクラスベース的継承ってどう書くのか、親クラスのメソッドの呼び出し方はどうか、モダンな書き方ってどうなのか、あらためて勉強してみた。

（この投稿ではES6やプロトタイパルな継承はあつかいません）

# 継承 #

[[JavaScript] そんな継承はイヤだ - クラス定義 - オブジェクト作成](http://qiita.com/LightSpeedC/items/d307d809ecf2710bd957)

↑の投稿が参考になりました。

結論としては、util.inherits([ブラウザバージョン](https://github.com/isaacs/inherits/blob/master/inherits_browser.js))のやり方がモダンな正しいクラス的継承。

```javascript
function inherits(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};
```

```javascript
function Parent(){}
function Child(){}

inherits(Child, Parent);

var c = new Child();
```

継承についてはほぼこれだけが、たったひとつの冴えたやり方といえるが、親クラスのメソッドの呼び出し方には、いろいろな流派があるようだ。

# 親クラスのメソッドの呼び出し #

## 基本 ##
```javascript
function Child(arg){
	Parent.call(this, arg);
}

Child.prototype.methodName = function(arg) {
	Parent.prototype.methodName.call(this, arg);
};
```

オーソドックスな呼び出し方だが、親クラスを明示的に書かなければならない。

## 少し改良 ##
上記inherits関数は、親クラスのコンストラクタを子クラスのコンストラクタのsuper_プロパティに保持するため以下のように書ける。

```javascript
function Child(arg){
	Child.super_.call(this, arg);
}

Child.prototype.methodName = function(arg) {
	Child.super_.prototype.methodName.call(this, arg);
};
```

明示的な親クラスの名前がソースコードから消えているのがわかる。
親コンストラクタの呼び出しは簡潔だが、ほかのメソッドからの親メソッドの呼び出しが長い。
ていうか、コンストラクタとそれ以外のメソッドで呼び出し方が違うのが気になる。

##  呼び出し方をそろえる ##

inherits関数で

```javascript
  ctor.super_ = superCtor;
```

↑こうなっている部分を仮に

```javascript
  ctor._super_ = superCtor.prototype;
```

と書き換えると以下のように書ける。

```javascript
function Child(arg){
	Child._super_.constructor.call(this, arg);
}

Child.prototype.methodName = function(arg) {
	Child._super_.methodName.call(this, arg);
};
```

呼び出し方は揃ったけど、あいかわらず長い。
また自分自身のクラスの名前を依然として明示的に書く必要がありモヤモヤする。

## thisに保持させる（ダメな例） ##

```javascript
function inherits(ctor, superCtor) {
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    },
    _super_: {
      value: superCtor.prototype,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};
```

こうしとけば、

```javascript
function Child(arg){
	this._super_.constructor.call(this, arg);
}

Child.prototype.methodName = function(arg) {
	this._super_.methodName.call(this, arg);
};
```

こう書けるんじゃね？
と一見思えなくもないが、

```javascript
function GrandChild(arg){
	this._super_.constructor.call(this, arg);
}

GrandChild.prototype.methodName = function(arg) {
	this._super_.methodName.call(this, arg);
};
```

継承が3階層以上になると2階層目のsuper呼び出しで無限ループで死ぬ。
GrandChildのインスタンスの`this._super_`が指しているのは常にChild.prototypeなのだから当然だ。

と、ここまでが前フリです。

# 歴史を振り返る #

## Douglas Crockfordの方法  ##

http://javascript.crockford.com/inheritance.html

```javascript
Child.prototype.methodName = function(arg) {
	this.uber("methodName", arg);
};
```

通常のメソッド内では上のように書けるが、コンストラクタ内では同じやり方で書けない。
initメソッドなどの初期化メソッドを用意してその中でuberメソッドを呼ぶ。

uberはメソッドの呼び出された階層の深さを記録して、その分だけprototypeチェーンをたぐっている。

## prototype.jsの方法 ##

http://prototypejs.org/learn/class-inheritance.html

```javascript
var Child = Class.create(Parent, {
	mathodName: function($super, arg) {
		$super(arg);
	}
});
```

メソッドの仮引数の先頭に$superがあるとそれを検知して、親クラスの同名メソッドを呼び出す関数を第一引数に渡す関数でメソッドをwrapするという黒魔術。
何を言ってるのかわからねーと思うが（略

## Josh Gertzenの方法 ##

http://joshgertzen.com/object-oriented-super-class-method-calling-with-javascript/

```javascript
var Child = Parent.extend({
	mathodName: function(arg) {
		arguments.callee.$.methodName.call(this, arg);
	}
});
```

メソッド自体に属性として親クラスのprototypeを持たせてそれをarguments.calleeで参照。
だがarguments.callee自体が非推奨であるため正直微妙。

##  Dean Edwardsの方法  ##

http://dean.edwards.name/weblog/2006/03/base/
http://dean.edwards.name/weblog/2007/12/base2-intro/

```javascript
var Child = Parent.extend({
	mathodName: function(arg) {
		this.base(arg);
	}
});
```

this.baseに格納した親クラスをメソッド呼び出し前に差し替え、呼び出し後に元に戻すというクロージャでメソッドをwrapするという黒魔術。
何を言ってるのかわからねーと思うが（略

##  John Resigの方法  ##

http://ejohn.org/blog/simple-javascript-inheritance/

```javascript
var Child = Parent.extend({
	mathodName: function(arg) {
		this._super(arg);
	}
});
```

メソッドの定義の中に_super呼び出しが存在すれば、this._superに格納した親クラスをメソッド呼び出し前に差し替え、呼び出し後に元に戻すというクロージャでメソッドをwrapするという黒魔術。
何を言ってるのかわからねーと思うが、prototype.jsとBase2のアイデアをミックスしたものらしい。

Dean EdwardsとJohn Resigの方法は、親クラスを保持するインスタンス変数を親クラスのメソッド実行時に差し替えるという共通のトリックを用いている。
しかし、親メソッド実行途中に例外が発生した場合には、親クラスを元に戻す過程が実行されなくなってしまう。

## Fiber.jsの方法 ##

https://github.com/linkedin/Fiber

```javascript
var Child = Parent.extend(function(base) {
	return {
		mathodName: function(arg) {
			base.methodName.call(this, arg);
		}
	};
});
```

親クラスをレキシカル変数に保持する方法。
記述は多少冗長になるが、リフレクションもどきやインスタンス変数の動的差し替えなどの黒魔術を使う必要がない。

ところでこの方法、初出はどなたなんでしょうか？
ご存じの方はお知らせください。
日本では右京webさんが2012/6に、ほぼ同様のアイデアを投稿されているようです。
http://hujimi.seesaa.net/article/278050880.html


# 結局どれを使えば良いのか？ #

以上、親クラスのメソッド呼び出しの主な手法をリストアップしてみました。
どのパターンを利用するかは、それぞれの得失を考慮した上での好みかな、と。

（記述のコンパクトさ重視なら、John Resigスタイルで、とか。）

# ところで #

今回、勉強しなおした成果（？）を、オレオレ継承ライブラリにしてみました。
Fiberスタイルですが名前空間を汚さないという個人的方針の為、クラスや継承の記述はもっとダサい感じに仕上がっています。

https://github.com/no22/qoo


そんじゃーね！