JavaScript Diary

継承 [ 2002/05/25 ]

「皆さん良いお年を」と書いて早6ヶ月経ちました f^^);
どうもお久しぶりです。優乃です。

何をやっていたかというと別にサボっていたわけではなく、訳あって非常に忙しい日々に追われておりました。

それではJavaScriptに関して何もやっていないかというとそうではなく、プログラムは書いてたので、多少の時間を利用してガリガリ書いております。
JSで圧縮(符号化)してみたり、暗号化してみたり、グラフ作成してみたり、等などです。
僕の問題はプログラムを書くのは好きなのですが、それをドキュメント化するのが非常に苦手という点でして、また気が向いたら少しずつ紹介していきたいと思います。

さて、本題に移りますが、今回は題名通り「継承」に関することです。
(WEB上の)JavaScriptで継承の機能を利用している人なんて正直見たことないのですが、ネットの進化や、.NETの台頭に伴い、継承が*使える*時代が来るのかもしれません。

現状のJavaScriptにおいて、今のところ継承するのにextendsなどの特別なキーワードはなく、継承を実現するための一番有名な方法とは以下のようなものだと思います。

function SuperClass(p0){
    this.p0 = p0;
}

SuperClass.prototype.m0 = function(){
    return this.p0;
};

function SubClass(p0, p1){
    this.constructor(p0);
    this.p1 = p1;
}

SubClass.prototype.m1 = function(){
    return this.p1;
};

SubClass.prototype = new SuperClass; // 継承

// new Class は new Class() と同値です

これを実行するとSubClassはSuperClassを継承したクラスになります。
誰もがこの方法には戸惑うと思いますが、prototypeベースであるが故の方法だと思って下さい。
割り切ってしまいたいものですが、どうしてもサラッと流せない点があります。
そうです、newです。
どう考えてもこれは不便で、JavaScriptの致命的な弱点となっているのではないでしょうか?

C++においては引数なしコンストラクタはデフォルトコンストラクタと呼ばれ、
特別な意味を持ち、なるだけシステムにあたり障りのないデフォルトコンストラクタを作成しておくことが重要だったりします。

勿論、JavaScriptにおいてもそういうスタンスは大切なことだと思いますが、だからと言って継承にnewすることが絶対条件なんてことは、OOPにおいてあってはならないことだと容易に分かります。
この場合そういう問題を置いておいても、僕なんかは必要もないのにインスタンスが生成されることにとても抵抗を感じます。

そこで今回の目的とは「SuperClassのインスタンス生成を回避しつつ継承のシステムを実現する」にしたいと思います。

いきなり結論にとびます(笑)
というのも実をいうとこれはStarry Nightで実現されています。
僕はそれを思い出しながら書いていますが、これは相当昔のことであるため、そのサイトが何処かも忘れてしまいました f^^); 申訳。

さてその方法とは以下のようなものだったと思います。

Function.prototype.inherit = function(super_class){
    function Temp(){}
    Temp.prototype = super_class.prototype;
    return this.prototype = new Temp;
};

これがコアな部分になります。使用方法は以下です。先ほどのSuperClassとSubClassを例に用います。

SubClass.inherit(SuperClass); // 継承

素晴らしいですね。完璧です。見事に問題を解決しています。
以下のようにそのままprototypeオブジェクトを取得できることも点数高いです。
var FP = SubClass.inherit(SuperClass);
あまりいい加減なことは書けませんが、Tempクラスを経由したという表現で正しいのでしょうか。

今回は紹介だけで留めるつもりでしたが、inherit関数を別の書き方で・・・以下

Function.prototype.inherit = function(super_class){
    return this.prototype = function(){
        if(super_class !== void 0 && this.constructor !== super_class){
            arguments.callee.prototype = super_class.prototype;
            return new arguments.callee;
        }
    }();
};

某ブラウザを弾けばもちょっとシンプルに書けますが、改悪している可能性が高いので、特に説明はしません。やってることは全く同じです。



サンプル集のページですが、初めてDOMアクセスをそれっぽく有効利用してみました(ソース参照)。
あーDOMって便利。