2006 年 8 月 2 日 22 時 22 分

継承とプロトタイプ


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


昨日の MixiError 型は、例外としては利用できるのだが、
以下のような課題を抱えている。

・MixiError は Error 型とはみなされない。
 new MixiError() instanceof Error が false になる。

・Error 型で定義されているメソッドと同様のものを、
 いちいち実装しなおすという手間がかかる。

特に前者は、ポリモルフィズムを利用して、
Error 型から派生した例外を利用する際に問題となる。

こういう場合、Java や C# では、継承を使って解決する。
それは JavaScript でも同じだ。
実際、昨日紹介した、SyntaxError 型などは、
Error 型から派生しているのだ。
以下のコードを実行してみると良くわかる。

    var isDerived = new SyntaxError() instanceof Error;
    WScript.Echo(String(isDerived));

結果は、true と表示される。

これらの型は、JavaScript に組み込まれているので、
内部的に Error を継承するように実装されているのだ。

では、型の継承はどのように行われているのか。

JavaScript では、プロトタイプがそのカギを握っている。
プロトタイプについては、7/26 に書いたが、
もう一度整理してみよう。

http://mixi.jp/view_diary.pl?id=184201595&owner_id=2300658

JavaScript では、ある型に対して、
以下の 3 種類のオブジェクトを意識する必要がある。

    ・関数オブジェクト(コンストラクタ)
    ・プロトタイプオブジェクト
    ・インスタンスオブジェクト

コンストラクタとプロトタイプは 1 つしか存在せず、
それぞれ密接に関係している。

コンストラクタの prototype プロパティには、
プロトタイプが格納されており、
プロトタイプの constructor プロパティには、
コンストラクタが格納されている。

    function MixiError(message) {
        // 省略
    }

    // このコードは書く必要はない(既定で設定される)
    MixiError.prototype.constructor = MixiError;

そして、コンストラクタから量産できるインスタンスは、
コンストラクタの prototype プロパティの値を、
自分のプロトタイプとして「内部的」に格納する。

そのおかげで、インスタンスが持たないプロパティでも、
プロトタイプが持っていれば利用できる事を説明した。

この内部的な参照を、「⇒」を使って書くことにしよう。

    インスタンス⇒プロトタイプ


さて、プロトタイプはあらゆるオブジェクトが持つ。
あらゆるオブジェクトということは、
コンストラクタやプロトタイプも例外ではない。
つまり、プロトタイプもプロトタイプを持つのだ。

話がややこしくなってきたので、
具体的な MixiError 型に当てはめて考えてみよう。

まずはインスタンス。これは簡単だ。

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

次にコンストラクタ。コンストラクタは関数である。
関数は Function 型のインスタンスであるので、
以下のような参照が成り立つ。

    MixiError コンストラクタ⇒Function プロトタイプ

コンストラクタは prototype プロパティを持つが、
これは、インスタンス用のプロトタイプなので、
コンストラクタのプロトタイプではないことに注意。

そして、プロトタイプ。プロトタイプは、
既定では new Object() として作成されるインスタンスだ。
そのため、以下のような参照が成り立つ。

    MixiError プロトタイプ⇒Object プロトタイプ

以上から、次のような「連鎖」が起きていることがわかる。

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

このような連鎖の事を、プロトタイプチェーンと呼ぶ。

オブジェクトが持たないプロパティにアクセスした際は、
内部的に参照するプロトタイプが調べられるが、
この参照は連鎖を順に辿って行われる。

つまり、MixiError インスタンスは、
MixiError プロトタイプが持つプロパティだけでなく、
Object プロトタイプが持つプロパティも利用できるのだ。

Object プロトタイプもプロトタイプへの参照を持つが、
その値は null である。唯一の例外だ。
結果的に、Object プロトタイプは、
全てのオブジェクトのプロトタイプとして機能する。

ここで改めて SyntaxError 型について考えてみよう。
SyntaxError プロトタイプのプロトタイプは、
仕様を見る限り、Error プロトタイプである。

つまり、SyntaxError は以下のようなチェーンを持つ。

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

つまり、JavaScript での継承は、基底型のプロトタイプを
派生型プロトタイプがプロトタイプとして参照することで、
チェーンに接続されることを利用しているということだ。



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