2006 年 7 月 26 日 18 時 27 分

プロトタイプ


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


JavaScript はオブジェクト指向である。
しかし、Java や C# のようなクラスベースではなく、
プロトタイプベースの言語である。

クラスベースの言語にクラスがあるように、
プロトタイプベースの言語はプロトタイプがある。

では、プロトタイプとは何か。

プロトタイプは、オブジェクトの雛形である。
それ自身もオブジェクトで、プロパティを持つ。

JavaScript では、あらゆるオブジェクトが、
暗黙にプロトタイプオブジェクトへの参照を持つ。

あるオブジェクトのプロパティを参照した時、
オブジェクトが指定したプロパティを持たない場合は、
オブジェクトの参照するプロトタイプが調べられ、
もしプロトタイプがプロパティを持っていれば、
それが代わりに使用されるのだ。

つまり、プロトタイプがメソッドを持っている場合、
そのプロトタイプを参照する全てのオブジェクトで、
自動的にそのメソッドを利用することができるのだ。

因みに、プロパティを設定する際は、
常にそのオブジェクトに対して設定されるので、
プロトタイプに変更が行われることはない。

さて、まずは例を示そう。
昨日のコードを、プロトタイプを使って書く。

========== MixiSession.js ==========

// クラスのコンストラクタ
function MixiSession(mailAddress, password) {
    // 内部で利用する XMLHttpRequest
    this._client = new XMLHttpRequest();
}

// GET を発行して結果の HTML を返す
MixiSession.prototype.fetch = function (url, query) {
    // this._client.open(~) などの処理
};

// POST を発行して結果の HTML を返す
MixiSession.prototype.post = function (url, form) {
    // this._client.open(~) などの処理
};

========== end of MixiSession.js ==========

コンストラクタは、Function 型のオブジェクトである。
Function 型オブジェクトは「prototype」プロパティを持つ。
既定では、このプロパティには、コンストラクタごとに
独立した空のオブジェクトが設定されている。

オブジェクトが内部的に持つプロトタイプへの参照は、
プログラムから直接アクセスすることはできないが、
コンストラクタから作成されたインスタンスには、
それが暗黙に参照するプロトタイプとして、
コンストラクタの prototype プロパティが設定される。

つまり、コンストラクタの prototype プロパティが示す
プロトタイプオブジェクトにプロパティを設定しておけば、
インスタンスに対してメソッドを設定しなくとも、
インスタンスが参照するプロトタイプを経由して、
結果的にインスタンスから利用することができるのだ。

プロパティに関数(オブジェクト)を設定する方法は、
既存のグローバル関数への参照を設定するだけではなく、
動的に関数を作成して設定する方法もある。

関数は、他のオブジェクトと同様、
コードの好きなところでインスタンスを作ることができる。
関数を動的に作るには、function 構文を使って書く。

function 構文は新しく関数を作成するので、
それを変数に格納したり、別の関数に渡したりして利用する。
オブジェクトにメソッドを作成するのは典型的な例だ。

  プロパティ = function (引数) { 処理 };

この function 構文は、関数式や関数生成式と呼ばれ、
文が実行されたときにその場で定義される。

MixiSession 関数と比較して欲しい。
MixiSession 関数は、関数宣言と呼ばれ、
あらゆるコードの実行に先立って先に定義される。
つまり、定義される(利用可能になる)タイミングが違う。

ちなみに、MixiSession 関数も、
関数式で定義することも可能である。

var MixiSession = function (mailAddress, password) {
    // 内容は同じなので省略
}; // 式なのでセミコロンで終わる

この場合、MixiSession よりも前で、
new MixiSession(~) などとするとエラーになる。

関数宣言は書ける場所が制約されているので、
俺は、グローバル関数には関数宣言を、
それ以外には関数式を使うようにしている。


まとめると、JavaScript でクラスを考える場合、
クラス毎に 3 種類のオブジェクトを意識する必要がある。

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

インスタンスは量産できるが、
コンストラクタとプロトタイプは 1 つだけ存在する。

コンストラクタは型であり、クラスを意味する。
プロトタイプは、コンストラクタの
「prototype」プロパティに格納されている。
コンストラクタから作成されたインスタンスは、
プロトタイプへの参照を内部に持つ。

クラスのように利用するためには、
以下の方針で行えばよい。

・クラス名と同じ名前の関数オブジェクトを作成し、
 コンストラクタに相当するコードを書く。

・インスタンスフィールドは、
 インスタンスオブジェクトのプロパティに設定する。

・インスタンスメソッドは、
 プロトタイプオブジェクトのプロパティに設定する。

なお、クラス(静的)フィールドやクラスメソッドは、
同様のものを定義する直接的な方法はないが、
コンストラクタのプロパティとして設定することで、
それっぽく利用することができる。

    MixiSession._version = 0.9;
    MixiSession.getVersion = function () {
        return MixiSession._version;
    };

    WScript.Echo(MixiSession.getVersion());

・クラス(静的)フィールドやクラスメソッドは、
 コンストラクタオブジェクトのプロパティに設定する。

このようにすることで、クラスベースの考え方を
JavaScript に適用することができる。



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