プロトタイプと継承/JavaScript

複数オブジェクトで、共通したプロパティやメソッドが使えると便利です。本項では、プロトタイプと継承について説明します。

尚、オブジェクトについては、「オブジェクトの使い方」をご参照下さい。

プロトタイプとは

 JavaScriptは、プロトタイプベースの言語と言われ、全てのオブジェクトが他のオブジェクトをプロトタイプ(原型)として作成されます。

プロトタイプとはオブジェクトの原型

 proto1を親として、子のyamadaを作る親子関係のようなイメージです。分かりやすい例は、Object.createを用いてオブジェクトを作成した時です。

【Object.createの利用例】
<script>
var proto1 = {
    country: "Japan"
};

var yamada = Object.create(proto1);
yamada.address = "Tokyo";
yamada.birthday = 2001;
</script>

 赤字部分は、proto1オブジェクトを作成しています。青字部分でproto1をプロトタイプとして、yamadaオブジェクトを作成しています。yamada.addressやyamada.birthdayは、プロパティの追加です。

 尚、Object.createはWindowsXPで使えるInternet Explorer8以前では使えません。Internet Explorer9以降や最新のFireFox、Google Chrome、Microsoft Edgeでは使えます。

継承

 オブジェクトは、プロトタイプを参照するようになっています。これを継承と呼びます。

プロトタイプによる継承

 上記は、yamadaオブジェクト自体にはcountryプロパティは存在しませんが、継承によりyamada.countryの値はJapanとなります。

 suzukiオブジェクトも、proto1をプロトタイプにして作成したとします。

【同じプロトタイプを使ってオブジェクトを作成】
var suzuki = Object.create(proto1);
suzuki.address = "Kyoto";
suzuki.birthday = 1999;

 addressとbirthdayプロパティの値は各オブジェクトで異なりますが、yamada.countryとsuzuki.countryは継承によって、同じJapanになります。このように、プロトタイプで作成したプロパティは、各オブジェクトで共通して利用出来ます。

 又、スクリプトの途中でproto1.country = "日本";を実行する事で、yamada.countryもsuzuki.countryも値が日本に変わります。つまり、プロトタイプでの変更は、継承する全てのオブジェクトに影響します。

 yamada.country = "日本";を実行すると、yamada自体にcountryプロパティが作成され、yamada.countryだけ日本に変わります。これは、オーバーライドと呼ばれます。

オーバーライド

 上記の場合、suzuki.countryはJapanのままです。つまり、そのオブジェクトにプロパティがない場合だけ、プロトタイプのプロパティを参照します。

コンストラクタ関数を使った継承

 今度は、ヤマダさんとスズキさんのオブジェクトをコンストラクタ関数を使って作成します。

【コンストラクタ関数を使った継承例】
<script>
function Peaple(address,birthday) {
    this.address = address;
    this.birthday = birthday;
}

Peaple.prototype.country = "Japan";
var yamada = new Peaple("Tokyo",2001);
var suzuki = new Peaple("Kyoto",1999);
</script>

 赤字部分のPeaple.prototypeは、Peapleのprototypeプロパティです。青字部分が実行された時、yamadaとsuzukiオブジェクトのプロトタイプになります。

コンストラクタ関数を使ったプロトタイプ説明

 プロトタイプのcountryプロパティは各オブジェクトで継承出来るため、yamada.countryもsuzuki.countryも値はJapanになります。

 関数Peaple(オブジェクト)のprototypeプロパティが、yamadaとsuzukiのプロトタイプオブジェクトになっている所がポイントです。

 尚、上記の方法はObject.createと違って、Internet Explorer8でも使えます。

プロトタイプチェーン

 プロトタイプから作られたオブジェクトを、更にプロトタイプにしてオブジェクトを作成出来ます。このような連鎖をプロトタイプチェーンと言います。

Object.createを使ったプロトタイプチェーンの説明

 以下は、プロトタイプチェーンの例です。

【Object.createを使ったプロトタイプチェーン例】
<script>
var proto0 = {
    country: "Japan"
};

var proto1 = Object.create(proto0);

var yamada = Object.create(proto1);
yamada.address = "Tokyo";
yamada.birthday = 2001;
</script>

 赤字部分は、proto0オブジェクトを作成しています。青字部分で、proto0をプロトタイプとしてproto1オブジェクトを作成しています。緑字部分は、proto1をプロトタイプとしてyamadaオブジェクトを作成しています。

 yamadaオブジェクトのプロトタイプの更にプロトタイプであるproto0にcountryプロパティがあるため、プロトタイプチェーンによりyamada.countryの値はJapanになります。

 次は、コンストラクタ関数を使った例です。

【コンストラクタ関数を使ったプロトタイプチェーン例】
<script>
function World() {
}
World.prototype.country = "Japan";

function Peaple(address,birthday) {
    this.address = address;
    this.birthday = birthday;
}

Peaple.prototype = new World();
var yamada = new Peaple("Tokyo",2001);
</script>

 赤字部分のWorld.prototypeは、Worldのprototypeプロパティです。青字部分が実行された時Peaple.prototypeのプロトタイプになります。緑字部分が実行されると、Peaple.prototypeをプロトタイプとして、yamadaオブジェクトが作成されます。

コンストラクタ関数を使ったプロトタイプチェーンの説明

 yamada→Peaple.prototype→World.prototypeとチェーンする事により、yamada.countryの値はJapanになります。又、Peaple.prototype.countryの値もJapanです。

プロトタイプのメリット

 ヤマダさんと、ヤマダさんが飼っている犬のハナコのオブジェクトで生年月日を表示するメソッドを作ったとします。

【コンストラクタでのメソッド定義】
<script>
function Peaple(address,birthday) {
    this.address = address;
    this.birthday = birthday;
    this.display = function() {
        document.write("生年月日は" + birthday + "年です。");
    }
}
function Dog(owner,birthday) {
    this.owner = owner;
    this.birthday = birthday;
    this.display = function() {
        document.write("生年月日は" + birthday + "年です。");
    }
}
var yamada = new Peaple("Tokyo",2001);
var hanako = new Dog("yamada",2010);
</script>

 上記は、yamada.display();でもhanako.display();でも「生年月日は〜〜です」と表示されますが、赤字部分のようにPeapleでもDogでもメソッドの定義が必要です。

 プロトタイプを利用すると、以下のようにメソッドを共通化出来ます。

【プロトタイプを使ったメソッド定義】
<script>
function Display() {
}
Display.prototype.display = function() {
        document.write("生年月日は" + this.birthday + "年です。");
}

function Peaple(address,birthday) {
    this.address = address;
    this.birthday = birthday;
}
Peaple.prototype = new Display();

function Dog(owner,birthday) {
    this.owner = owner;
    this.birthday = birthday;
}
Dog.prototype = new Display;

var yamada = new Peaple("Tokyo",2001);
var hanako = new Dog("yamada",2010);
</script>

 赤字部分は、共通で利用するメソッドです。青字部分が実行された時、Display.prototypeがPeaple.prototypeやDog.prototypeのプロトタイプになります。

 緑字部分が実行されると、Peaple.prototypeはyamadaオブジェクトのプロトタイプ、Dog.prototypeはhanakoのプロトタイプになります。従って、継承によってyamada.display();でもhanako.display();でも「生年月日は〜〜です」と表示されます。

プロトタイプのメリットは、メソッドを共通化出来る事

 このように、プロトタイプを利用するとメソッドを共通化出来るため、オブジェクトを作る際に1回1回定義する必要がなくなります。又、修正が必要な場合でも、共通化したメソッドだけの修正で済みます。

Object.getPrototypeOf

 プロトタイプにしているオブジェクトは、Object.getPrototypeOfで確認出来ます。

【Object.getPrototypeOfの利用例】
<script>
var proto1 = {
};
var yamada = Object.create(proto1);
document.write(Object.getPrototypeOf(yamada) == proto1);
</script>

 赤字部分で、yamadaオブジェクトはproto1をプロトタイプにして作成しています。このため、青字部分ではtrueと表示されます。

 因みに、proto1は何をプロトタイプにしているかと言えば、デフォルトで存在するObject(コンストラクタ)のprototypeプロパティです。このため、document.write(Object.getPrototypeOf(proto1) == Object.prototype);を実行すると、trueと表示されます。

JavaScriptがプロトタイプベースと呼ばれる理由

 最初にObjectが存在し、そのprototypeプロパティをプロトタイプオブジェクトにして次々子や孫のオブジェクトが作成されます。これが、JavaScriptがプロトタイプベースと呼ばれている理由です。

前のページクロージャ

  • このエントリーをはてなブックマークに追加