2006 年 8 月 5 日 23 時 23 分

継承の実践 #3


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


なんか継承で長々とやってるな。もう一息。

プロトタイプを継承する方法は分かった。
プロトタイプには、主にメソッドが登録されているため、
親クラスのメソッドを継承することができる。

では、インスタンスプロパティなど、
プロトタイプに定義されていないものを
親クラスから継承するにはどうすればよいか。

Java や C# では、派生クラスのコンストラクタから、
基底クラスのコンストラクタを呼び出すことで、
基底クラスが持つインスタンスフィールドの値を継承する。

JavaScript でも考え方は同じだ。

    function MixiError(message) {

        // Error のコンストラクタを呼び、
        // this を初期化したい
        Error(message); // ?

    }

残念ながら、上記ではうまくいかない。
単純に Error に括弧をつけて呼び出した場合、
通常の関数呼び出しとなってしまうので、
this として MixiError インスタンスを渡すことができない。

だからといって、new Error(~) とすると、
Error の新しいインスタンスが作成されてしまい、
その戻り値は MixiError のインスタンスにならない。

JavaScript の関数オブジェクトには、
こういう用途に使うための、
call メソッド(プロパティ)がある。
そう、「関数オブジェクトが持つメソッド」だ。

call メソッドは、第 1 引数にオブジェクトを取る。
第 2 引数以降は、本来の関数が持つ引数と同じである。
第 1 引数として渡したオブジェクトは、
関数の内部で、this として参照することができる。
つまり、this を明示することができるのである。

ややこしいので、書いて比較してみよう。

●Error("message")

 Error 関数が受け取る引数は文字列 "message"。
 Error 関数内の this は、グローバルオブジェクトを指す。

●new Error("message")

 Error 関数が受け取る引数は文字列 "message"。
 Error 関数内の this は、新しい Error インスタンス。

●Error.call(object, "message")

 Error 関数が受け取る引数は文字列 "message"。
 Error 関数内の this は、object。

では、call メソッドを使って、
MixiError コンストラクタを書いてみよう。

    function MixiError(message) {

        // Error インスタンスとしての初期化
        Error.call(this, message);

    }

MixiError のインスタンスである this を、
Error コンストラクタにそのまま渡すことで、
Error インスタンスとしての初期化を行うのだ。
「理論的」にはこれでうまくいく。

理論的と書いたのには意味がある。

仕様上では、Error コンストラクタは
message の引数を 1 つ取り、
undefined でなければ、新しいインスタンスの
message プロパティに文字列として格納するとある。

しかし、Internet Explorer や、FireFox では、
上記コンストラクタで作成した MixiError インスタンスの、
message プロパティが設定されていないようだ。
つまり、Error コンストラクタが旨く機能していない。

まあ、Error 型は組み込み型であるので、
その実装方法は実行環境によって異なる。
なので、派生コンストラクタが、Error コンストラクタを
call で呼び出すことを想定していない可能性もある。

なので、MixiError コンストラクタに関しては、
ちゃんと初期化も行った方がよさそうである。

    function MixiError(message) {

        // Error インスタンスとしての初期化
        Error.call(this, message);

        // MixiError インスタンスとしての初期化
        if (message !== undefined) {
            this.message = String(message);
        }

    }

自分で設計したクラスから派生させる場合は、
派生クラスのコンストラクタ内で、
call で基底クラスのコンストラクタを呼ぶことで、
インスタンスプロパティの継承を実現することができる。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.