2006 年 8 月 3 日 22 時 28 分

継承の実践 #1


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


MixiError を Error から継承するためには、
MixiError のプロトタイプチェーンに
Error プロトタイプを追加すればよいことがわかった。

つまり、次のようなチェーンになれば良い。

    MixiError インスタンス
        ⇒MixiError プロトタイプ
            ⇒Error プロトタイプ
                ⇒Object プロトタイプ

ただ、これは単純には実現できない。

MixiError プロトタイプは、既定では、
new Object() で作成される Object のインスタンスである。
(厳密には、constructor プロパティも自動的に設定される)
つまり、MixiError プロトタイプ⇒Object プロトタイプだ。

MixiError プロトタイプが、Object プロトタイプではなく、
Error プロトタイプを参照するように変えるには、
内部的な参照を書き換える必要があるが、
これはプログラムコードからアクセスすることができない。

では、どうすればよいか。

MixiError プロトタイプは、MixiError コンストラクタの
prototype プロパティに格納されている。
prototype プロパティは読み取り専用ではないので、
必要であれば、好きなオブジェクトを使うことができる。

なので、Error プロトタイプを参照するオブジェクトを、
MixiError プロトタイプとして使えばよいのだ。

そのようなオブジェクトとして最初に思いつくのは、
Error インスタンスである。

    function MixiError(message) {
        // 省略
    }

    MixiError.prototype = new Error(); // ダミー
    MixiError.prototype.constructor = MixiError;

    // name には例外型の名前が入る。
    MixiError.prototype.name = "MixiError";

    // その他のプロトタイププロパティの設定
    MixiError.prototype.hoge = "fuga";

プロトタイプは、constructor プロパティを持ち、
値としてコンストラクタへの参照を保持しなければならない。

既定で作られる MixiError.prototype は、
適切な constructor プロパティを持つのだが、
今回は、プロトタイプ自体を置き換えているので、
改めて constructor プロパティへの代入が必要だ。

こうすると、以下のようなチェーンとなる。

    MixiError インスタンス
        ⇒MixiError プロトタイプ(=Error インスタンス)
            ⇒Error プロトタイプ
                ⇒Object プロトタイプ

では試してみよう。

    var instance = new MixiError("hogehoge");
    WScript.Echo(instance.toString());

実行すると、[object Error] と表示された。
もし、Object プロトタイプの toString が呼ばれていたら、
[object Object] と表示されるはずなので、
Error プロトタイプの toString が呼ばれているのは確かだ。

これはうまくいっているように見える。
実際、MixiError 型には何も問題がない。
問題がどこにあるかというと、
Error インスタンスを作成しているという点だ。

たとえ MixiError インスタンスを利用しなかったとしても、
MixiError プロトタイプは存在するため、
余計な Error インスタンスが 1 つ作成されてしまうのだ。

Error 型は自分で作った型ではないので、
Error インスタンスがどんなプロパティを持つか分からない。
また、インスタンスを作成することによって、
どのような処理が行われるかどうかもわからない。

これは、例えば Error コンストラクタが、
自分のインスタンスの数を数えていたり、
例外を送出するために特別なコードを実行したりする場合、
目に見えないところで問題が起きる可能性が考えられる。

また、Error インスタンスがプロトタイプになっているので、
Error プロトタイプが持たず、
Error インスタンスのみが持つプロパティまで、
MixiError のプロトタイプとなってしまう。

例えば、FireFox の Error インスタンスは、
stack というプロパティを持つ。
これは例外が起きた場所を特定するための情報であり、
Error プロトタイプは、このプロパティを持っていない。

もし、MixiError を上記のように実装した場合、
Error インスタンスである MixiError プロトタイプが、
stack プロパティを持ってしまうことになり、
それは MixiError インスタンス全てに影響してしまうのだ。



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