prototype.js の Class 拡張試案: Class.extend

Prototype の拡張試案の一つとして、これまでにクラスの継承のための仕組み (Class.extend メソッド) を提唱してきました。

これは、実際にはクラスのコンストラクタに割り当てられ、

MyClass = Class.create();
...
AnotherClass = MyClass.extend(...);

という風に、個々のクラスをレシーバ (メソッドの受け取り手。ここでは MyClass) として呼び出すことを想定したものです。

この「継承」とは別の話で、クラスの「拡張」をしたい場合もありますよね。

Prototype ではどちらも Object.extend というメソッドを使って行われます。例:

Object.extend(Array.prototype, Enumerable);

私も「拡張」に関してはこの方法を使っていました。

これを、「クラスの拡張」ということがハッキリ分かるようにしたいと以前から思っていたんです。で、Class.extend を流用する方法というのを思い付きました。

Class.extend が、本来の用法と違って "Class" をレシーバとして呼ばれた場合は「拡張」を行うようにする、というものです:

var Class =
...
    extend: function (subobj)
    {
	if (this == Class)
	    return this.extend_klass.apply(null, arguments);
	...
    },
    extend_klass: function (klass, proto)
    {
	Object.extend(klass.prototype, proto);
    },

分かりやすさのためにクラス拡張用のメソッド (extend_klass) を独立させましたので、"extend" を経由せずにそちらを直接呼んでも構わないんですが、レシーバによって "this" の意味が変化する、という面白さを感じていただけるんじゃないかと思います。

この変更により、上の配列クラスの拡張例は

Class.extend(Array, Enumerable);

と書き換えられるようになります。クラスの拡張であることが明確な事と、"prototype" の指定がメソッド内に隠蔽される、というごく些細なメリットがあります。

(問題があるとすれば、「常に prototype に追加される」という点でしょうか…? 逆説的ですが)

感覚的には、Ruby でクラスをいつでも開いて自由に定義を変更する、という感じで使えると思います。