superが呼べる継承メソッド。

javascriptで継承といえば、

var Parent = function() {};
var Child = function() {};
Child.prototype = new Parent();

という感じで「prototype継承」を使うのが普通(だと思う)。


ただ、この手法で困るときがある。
オーバーライドした子クラスのメソッド中で親クラスの同名メソッドを呼びたいとき。

Child.prototype.method = function() {
  Parent.prototype.method.apply(this);
};

一応上記の方法で子クラスのインスタンスのコンテキストで呼ぶことはできる。
ただ、記法が冗長になってしまうのと、
親クラス名をハードコーディングするか、プロパティ等に保持していないといけない。


というわけで以下のように使える継承メソッドを作ってみた。

var Parent = function() {};
Parent.prototype = {
  name:   'Parent',
  method: function() {
    alert(this.name);
  }
};

var Child = (function() {}).inherit(Parent);
Child.proto({
  name:   'Child',
  method: function() {
    this.super().method();
  }
});

var p = new Parent();
var c = new Child();

p.method(); // Parent
c.method(); // Child

Functionオブジェクトからinherit()を呼ぶと、引数で与えたFunctionオブジェクトを継承する。
また、proto()メソッドで既存のprototypeに要素を追加できる。

子クラスのメソッドからsuper()を呼ぶと、
自分自身のコンテキストで親クラスのメソッドを呼べるオブジェクトが返る。
なお、何も継承していないクラスのオブジェクトから呼ばれた場合は、Objectを親クラスの代わりに使用する。



(´・ω・`)しかし世の中に既にあったりしないんだろうか


id:amachangさんにレビューしてもらえたらなぁ・・・という願望。


コードの中身は以下。

/**
 * inherit.js - utility for class inheritation
 */

(function() {
    Object.prototype.proto = function(proto) {
        for (key in proto) {
            this.prototype[key] = proto[key];
        }

        return this;
    };

    Object.prototype.inherit = function(s) {
        if (typeof(s) != 'function') throw new Error('cannot inherit from non-function variable');

        var f = function() {};
        f.prototype = new s();

        var p = this.prototype;

        this.prototype = new f();
        this.proto(p);
        
        this.prototype.__super_proto__ = s.prototype;
        
        return this;
    };
    
    Object.prototype.super = function() {
        if (typeof(this.__super_proto__) != 'object') this.__super_proto__ = Object.prototype;
        
        if (typeof(this.__super__) != 'object') {
            var s = {};
            var p = this.__super_proto__;
            
            for (key in p) {
                if (typeof(p[key]) != 'function') {
                    s[key] = p[key];
                    continue;
                }

                s[key] = (function(origin, target, method) {
                    return function() {
                        var args = Array.prototype.slice.call(arguments);

                        return (this === origin) ? method.apply(target, args) : new method(args);
                    };
                })(s, this, p[key]);
            }

            this.__super__ = s;
        }

        return this.__super__;
    };
})();