いまをときめくJavaScriptに関する知識と技術の向上を目指しながら、
インフォニック発JavaScriptライブラリを世界に発信することを目論むプロジェクトです。

他にもWebアプリケーションにおけるUI構築技術全般をターゲットにしています。

公開日:2008.01.21

クラス - 継承

関数(クラス)のprototypeプロパティ

全ての関数オブジェクトは prototypeというプロパティを持っています。

この prototypeプロパティはどういった事ができるか。 以下のコードを見てください。

function Dog(){}

Dog.prototype.bow = function(){ alert('ワン') };

var d = new Dog();
d.bow(); //-> ワンと表示

Dog関数(クラス)を作成し、この関数の prototype プロパティの プロパティbowに関数をセットしています。 そしてnewにより Dog オブジェクトを作成し、そのオブジェクトの bow を呼ぶと prototype のプロパティであるbowにアクセスできましたね。

全てのオブジェクトはクラス(関数)のprototypeに暗黙の参照を持ち、 オブジェクトのプロパティにアクセスした際に prototype のプロパティも参照できる事になっています。

またオブジェクトをいくつ作成したとしても、 クラスのprototypeは一つだけとなりますので、 関数などはこの prototype にまとめておく事で効率よく処理できます。

prototype を利用したクラスの作成

では prototype を使って前回のクラスを書きなおしてみましょう。

function Chicken(name){ this.name=name }
Chicken.prototype = {    // オブジェクトリテラル形式でprototypeに代入
  crow: function() {
     alert("こけっ");
  },

  lay: function() {
     alert(this.name + "は卵を産んだ");
  }
};

var kimu = new Chicken("きむさん");
kimu.crow(); //-> こけっ
kimu.lay();  //-> きむさんは卵を産んだ

前回の書き方に比べて(見た目が)より一層クラスっぽくなりましたね。 このように prototype には直接オブジェクトを代入する事もできます。

prototypeを利用した継承

prototypeを利用してクラスの継承を行う事ができます。 まずはコードを見てみましょう。

function Parent(){ /* */ }
Parent.prototype = {
  name: "親",
  show: function(){ alert(this.name); }
}

function Child(){ /* */ }
Child.prototype = new Parent(); // ChildクラスのprototypeにParentオブジェクトをセット
Child.prototype.name = "子";

var c = new Child()
c.show(); //-> 子と表示

「子」が表示された理由はわかりましたでしょうか?

ChildクラスのオブジェクトはChildクラスのprototype、 つまりParentクラスのオブジェクトが持つプロパティ全てに Child.prototype 経由で暗黙的にアクセスする事ができます。 この全てにというのにはParentクラスのprototypeオブジェクトのプロパティも含まれます。

それを踏まえて上記のChildオブジェクト(c)のshowを参照した時の、 値の参照順序は以下の通り。

  1. Childオブジェクトのshow
  2. Childクラスのprototype(Parentオブジェクト)のshow
  3. Parentクラスのprototype(実はObjectオブジェクト)のshow

この中で一番最初に見つかったプロパティ値を参照することになっています。 この場合は1にも2にも show が見つからないので 3の Parent.prototype の show が参照されました。

ちなみに1にも2にも3にも該当のプロパティが見つからない場合は 「undefined」という特別な値を返します。

また show の中で this.name を参照していますが、 この時も c.show の時点で this は c を指しているので、 たとえ show が Parent.prototype のプロパティであっても、 以下の順に参照を行います。

  1. Childオブジェクトのname
  2. Childクラスの prototype の name
  3. Parentクラスの prototype の name

そして Childクラスの prototype と Parent クラスの prototype の両方に name がセットされてますが優先順により2のnameが参照されて「子」を表示したのです。 クラスベース風に表現すると「nameプロパティをオーバーライドした」という事になります。

このように prototype を利用すると擬似的に継承関係をシミュレートできます。

さらに継承させる

function GrandChild(){ };
GrandChild.prototype = new Child();
GrandChild.prototype.name = "孫";

var gc = new GrandChild();
gc.show(); //-> 孫と表示

いくつ継承させようがprototypeを辿って 一番最初に見つけたプロパティを正しく参照できる事がわかります。

オブジェクトのクラスのprototype
⇒そのオブジェクトのクラスのprototype
⇒⇒そのオブジェクトのクラスのprototype
・・・以下どこまでも。

このようにprototypeを次々に辿ってプロパティを参照する仕組みを 『プロトタイプチェーン』と呼びます。

まとめ

  • 全ての関数オブジェクトは暗黙で prototypeプロパティを持つ。
  • オブジェクトに参照するプロパティがない場合、クラスの prototypeのプロパティを参照する。
  • クラスの prototypeはそのクラスから作成されたオブジェクト間で共有される。
  • prototypeを辿っていき最後までプロパティが見つからない場合はundefinedという値になる。