javascriptのオプジェクト指向とかプロトタイプについて(とある事情により)急いで勉強しているsasata299です。こんばんわ。(こんばんわー!)

プロトタイプを使うと何が嬉しいのか。この2点だと思います。

1. 継承関係を簡単に作れるため、オブジェクト指向で書きやすい
2. メモリを無駄に使用しないため、パフォーマンスが良い

では、早速始めましょう。(*・ω・)ノ

そもそもjavascriptでは全てのデータがオブジェクトです。配列もハッシュも関数もオブジェクトです。
そして、オブジェクトとは全てハッシュなのです。(配列はキーが数字なだけのハッシュ)

■ オブジェクトの例

例1
var obj = {}; // 空のオブジェクト

例2
var person = {
    name: "sasata299",
    say: function() {
        alert("My name is " + this.name + ", thank you.");
    }
}

person.say(); // Myname is sasata299, thank you.

先ほど、オブジェクトとは全てハッシュだと言いました。しかし、関数オブジェクトはハッシュと言えるのでしょうか・・?

■ 関数オジェクトはクラス!?

var Hoge = function() {}; // 関数オブジェクトがクラス

実は、関数オブジェクトは特殊なオブジェクトで、クラスの役割をします。そして、関数オブジェクトに対してnewすると、初期化されたオブジェクトが生成されます。関数オブジェクトはコンストラクタなのです。

var hoge = new Hoge(); // hogeはHogeクラスのオブジェクト

ここまでは簡単ですね。ここからプロトタイプの話に入りますー。

新規のオブジェクトを生成する場合、普通に考えればコンストラクタを使うと思います。

■ コンストラクタ

var Human = function(name) {
    this.name = name;
    this.say = function() {
        alert("My name is " + this.name + ", thank you.");    
    };
};

var human = new Human("sasata299");
alert(human.name); // sasata299
human.say(); // Myname is sasata299, thank you.

humanオブジェクトを生成してみました。これでも別に悪くはありません。まぁ、普通ですね。

ただ、プロトタイプを使うと、オブジェクト指向な書き方(継承とか)が簡単に出来るんです。プロトタイプというのは、そのクラスがデフォルトで持っているプロパティを定義したprototype(原型)のことです。

■ プロトタイプ

先ほどのコンストラクタでの例を、プロトタイプを使って書き直してみるとこんな感じになりますー。
このように書くことで、Humanクラスのオブジェクトは、nameプロパティはもちろん、sayプロパティ(メソッド)も自動的に定義されます。

var Human = function(name) {
    this.name = name;
};
Human.prototype = {
    say: function() {
        alert("My name is " + this.name + ", thank you.");
    }
};

var human = new Human("sasata299);
alert(human.name); // sasata299
human.say(); // Myname is sasata299, thank you.

Human.prototypeにプロパティを設定することで、Humanクラスのオブジェクトには自動的にそのプロパティが定義されるというわけです。プロトタイプをどのように参照しているのかは、こちらのサイトが分かりやすかったです。

さて、次はプロトタイプを使って、オブジェクト指向な設計をしてみましょう。

■ プロトタイプを使ったオブジェクト指向

var Animal = function() {
    this.hoge = 10;
};
Animal.prototype = {
    name: "noname",
    age: 20,
    bark: "gaoooo!!"
};

var Dog = function(age) {
    this.age = age;
};
Dog.prototype = new Animal(); // Dogクラスの親クラスがAnimalクラス
Dog.prototype.bark = "wanwan!!"; // barkプロパティをオーバーライド

var dog = new Dog(60);
alert(dog.name); // noname
alert(dog.age); // 60
alert(dog.bark); // wanwan!!
alert(dog.hoge); // 10(コンストラクタで設定したプロパティも継承される)

ポイントは13行目。Dogクラスのプロトタイプに、Animalクラスのオブジェクトを指定します。

Dog.prototype = new Animal();

たったこれだけで継承関係の出来上がりです。簡単ですね。(*´Д`*)

上の例でもわかるように、プロパティを定義するにはコンストラクタで定義する方法と、プロトタイプで定義する方法とがあります。どちらでも良さそうですが、コンストラクタの中でプロパティを定義する場合、メモリの問題があります。値が固定のものであれば、プロトタイプに定義するべきです。

例えば、上のコンストラクタの例では、コンストラクタに対してnewしたとき、毎回同じ関数オブジェクトを生成し、sayと言う名前でオブジェクト自身(this)に代入します。複数オブジェクトを生成するような場合、nameプロパティのように毎回値が変わるものならしょうがないですが、同じ処理を行う関数オブジェクトをオブジェクト毎に生成するのはメモリの無駄です。プロトタイプを使えば、オブジェクト自身には値を持たず、共通のプロトタイプの値を参照するようになるため、メモリ使用量が抑えられます。

■ プロトタイプの再定義とオーバーライド

最後に、僕がしばらく「うーんうーん」と悩んだところを紹介します。こんなケースを考えてみます。

var Box = function() {};
Box.prototype = { color: "red" };

var redBox = new Box();
alert(redBox.color); // red

Box.prototype = { color: "blue" };

var blueBox = new Box();
alert(redBox.color); // red
alert(blueBox.color); // blue

Box.prototype.color = "yellow";

alert(redBox.color); // red
alert(blueBox.color); // yellow

まず、4行目でredBoxオブジェクトを生成しています。この時点ではcolorは当然「赤」です。次に、7行目でBoxクラスのプロトタイプを「青」で設定しています。9行目でblueBoxオブジェクトを生成すると、今度はcolorが「青」になっていますね。

これは一体どういうことなのか??

javascriptでは、newしたときにプロトタイプの値を参照し、この値をいつまでも参照し続けます。例えば、redBoxオブジェクトは、new時のプロトタイプ

Box.prototype = { color: "red" };

の { color: "red" } をプロトタイプとして参照し続けます。7行目のようにプロトタイプの中身が変更されても関係ありません。

Box.prototype = { color: "blue" };

これは、Boxクラスのオブジェクトを生成するときに、「プロトタイプとして現在利用するプロトタイプは { color: "blue" } ですよ」ということを定義しているだけです(※ { color: "blue" } というオブジェクトにBox.prototypeという名前を付けている感じ)。redBoxオブジェクトとは全く関係ありません。redBoxが参照しているプロトタイプもそのまま無名オブジェクトとなって残っています。

Box.prototype.color = "yellow";

この部分はちょっと注意です。

javascriptでは、プロパティを参照するとき、以下の4つの手順で値を検索します。
※プロパティが見つかった場合、検索を終了して、値を返します。

1. そのプロパティがオブジェクトに直接定義されていないかチェック。
2. そのオブジェクトのprototypeプロパティとして定義されていないかをチェック。
3. 継承ツリーを辿り、最終的には最上位のObjectオブジェクトのプロパティまでチェック。
4. それでも見つからない場合、undefinedを返す。

例えば、bluBox.colorを検索する場合、このような検索になります。

1. blueBox.color // 見つからないのでプロトタイプを探しに行く
2. Box.prototype.color // 見つかったー!!

なので、Box.prototype.colorを変更した場合には、(同じプロトタイプを持つ)コンストラクタから生成されたオブジェクトは全て影響を受けます。そのため、このような結果となります。

alert(blueBox.color); // yellow

【参考にしたサイト】
そろそろきっちりJavaScript
JavaScriptのオブジェクト指向:プロトタイプをきちんと理解する
Javascript初心者からみたprototype
続・Javascript初心者によりprototype

この本も参考にしました〜。( ´_ゝ`)

まるごとJavaScript & Ajax ! Vol.1まるごとJavaScript & Ajax ! Vol.1
著者:天野 仁史
販売元:インプレスジャパン
発売日:2007-02-15
クチコミを見る
このエントリーをはてなブックマークに追加