JavaScript は プロトタイプベースのオブジェクト指向言語です。
今回は、JavaScript の プロトタイプについて紹介します。
なお、未経験からITエンジニアへの就職に興味がある方や未経験からプログラミングを効率よく学びたいと考えている方は、就職率98.3%で受講料無料のプログラミングスクールプログラマカレッジもおすすめです。
目次
原型という意味を持つ「 プロトタイプ 」は、JavaScript では、原型となるオブジェクトを意味します。
プロトタイプ( 原型となるオブジェクト )を元に、新しいオブジェクトを作成していくような構造になっているので、「 JavaScript は プロトタイプベースのオブジェクト指向言語 」と言われます。
オブジェクト指向という概念は 奥が深いので、詳しい説明は割愛しますが、簡単なイメージとしては、共通の目的を持った モノ( オブジェクト )を、セットで扱うことで、より効率良く 且つ シンプルなプログラム を組む という考えです。
そして、オブジェクト指向のプログラミング言語は、JavaScript の他に Java や C++ があります。
Java や C++ は、オブジェクト( 共通の目的のためにセットとなったモノ )を定義する方法として、「 クラス 」という概念を用います。
全てのオブジェクトにはクラスという鋳型が必ず存在し、そのクラスを実体化することでオブジェクトは生成される、という考え方です。
したがって、Java や C++ のオブジェクトは 必ずクラスが存在し、オブジェクト内のメンバも クラスの定義から逸脱する事ができません。
そして、オブジェクトはクラスから実体化されるので「 インスタンス 」と呼ばれます。
このように、クラスによって オブジェクトの実体であるインスタンスを生成する という概念を用いる オブジェクト指向言語のことを、「 クラスベースのオブジェクト指向言語 」といいます。
これに対して、JavaScript では、オブジェクトは 原型( プロトタイプ )となるオブジェクトに 独自の特徴を付加することで生成する、という考え方をします。
このようなオブジェクト指向言語のことを、「 プロトタイプベースのオブジェクト指向言語 」といいます。
このようにオブジェクトに対する考え方に違いがあるため、JavaScript と Java や C++ では オブジェクトの性質が異なります。
JavaScript では、オブジェクト自身が独自の特徴を付加するので、オブジェクトに直接メンバを追加したり削除したりすることができます。
JavaScript でオブジェクトを生成する際には、コンストラクタとnew 演算子を使用します。
コンストラクタとは、関数オブジェクトのことです。
全ての関数は、オブジェクトを生成するコンストラクタになる可能性があります。
オブジェクトは、次のように記述して生成します。
function sampleFun() {}
let obj = new sampleFun();
function で定義した関数に、new 演算子を付けて呼び出しているだけです。
こうすることで、新しいオブジェクトが生成され、返り値として戻ります。
return 文を記述しなくても、生成されたオブジェクトが返される点に注意して下さい。
もう少し詳しくコンストラクタの構造を見てみます。
通常、コンストラクタとして呼ばれる関数の中では、生成しようとするオブジェクトの初期化を行います。
共通のプロパティなどを、次のように コンストラクタ内で定義します。
function sampleFun() {
this.prp1 = "こんにちは!";
this.prp2 = "こんばんは!";
}
let obj1 = new sampleFun();
alert(obj1.prp1); // こんにちは!と表示される
let obj2 = new sampleFun();
alert(obj2.prp2); // こんばんは!と表示される
コンストラクタとして関数オブジェクトを実行した場合、this には今新たに生成されようとしているオブジェクトが入ります。
ですので、this にプロパティを設定すると、new して作成されるオブジェクトの初期定義を行うことができます。
このように、同じコンストラクタを用いて複数のオブジェクトを生成すると、同じプロパティを持つオブジェクトを簡単に作成することができます。
プロトタイプベースのオブジェクト指向言語では、オブジェクトは 別のオブジェクトをプロトタイプとしてできていると考えます。
JavaScriptでは、この考え方を「 暗黙の参照 」という形で実現しています。
オブジェクトA をプロトタイプとしているオブジェクトB は、「 オブジェクトA に対し 暗黙の参照を持っている 」という状態になります。
次のような状態です。
let objectA = ...
// objectB は objectA に対し暗黙の参照を持っている
// (objectAはobjectBのプロトタイプである)。
let objectB = ...
objectA.prp = 10; // objectA の prp プロパティをここで設定
alert(objectB.prp); // 10 と表示される
上のサンプルは、あるオブジェクトがプロパティの評価をされる際に、自分自身がそのプロパティを持っていない場合は、暗黙の参照をたどって、その先のオブジェクトのプロパティを評価する ということを表しています。
これが JavaScript におけるプロトタイプの仕組みです。
具体的にプロトタイプを指定する機構を見てみましょう。ここで再びコンストラクタが出てきます。
全ての関数オブジェクトは、prototype というプロパティを持っています。
これはオブジェクトで、コンストラクタの prototype が持つプロパティは、インスタンスのプロパティとして参照することができます。
関数オブジェクトを定義した直後は、この prototype は、何もプロパティを持たないシンプルなオブジェクトを参照していますが、別のオブジェクトを代入したり、新たなプロパティを設定したりすることができます。
そして、その関数オブジェクトをコンストラクタとして生成されたオブジェクトは、コンストラクタの prototype プロパティに代入されているオブジェクトに対し、暗黙の参照を持ちます。
つまり、prototype が示すオブジェクトが、プロトタイプとなります。
prototype プロパティは、次のように記述して使用します。
function sampleFun() {}
// prototype オブジェクトに prp というプロパティを設定
sampleFun.prototype.prp = "こんにちは!";
let obj = new sampleFun();
alert(obj.prp); // こんにちは!と表示される
sampleFun によって生成されたオブジェクトは、sampleFun.prototype が指すオブジェクトを暗黙的に参照します。
ここで、あるコンストラクタを new してできるオブジェクトが 全てそのprototypeへの暗黙の参照を持つとすると、そのプロパティは new されたオブジェクトで共有されてしまうのではないか、という疑問が生じるかもしれません。
結論からいうと、確かに 共有はされますが、それが実際に問題になることはありません。
次のサンプルをご覧下さい。
function sampleFun() {}
sampleFun.prototype.prp = 10;
let objA = new sampleFun();
let objB = new sampleFun();
alert(objA.prp) // 10 と表示される
alert(objB.prp) // 10 と表示される
objA.prp = 20;
alert(objA.prp) // 20 と表示される
alert(objB.prp) // 10 と表示される
読み取り評価をする場合には 暗黙の参照をたどりますが、代入や delete 演算子を使用する場合には 暗黙の参照をたどりません。
ですので、「 objA.prp = 20; 」が実行された時点で、objA に、prp というプロパティが新たに作成されて、そこに 20 が代入されています。
よって、sampleFun.prototype.prp の値は変わらないまま( 10 )となります。
この仕組みが、プロトタイプベース言語である JavaScript のオブジェクトの根幹となります。
コンストラクタの prototype には、オブジェクトも代入できるので、prototype に代入したオブジェクトが 別のオブジェクトをプロトタイプにしていることもあります。
また、そもそも全てのオブジェクトは Object.prototype を暗黙的に参照しています。
このようなプロトタイプの連鎖のことを、プロトタイプチェーンといいます。
次のサンプルをご覧下さい。
let objA = new Object();
objA.prp = 10;
function fun1() {}
fun1.prototype = objA;
let objB = new fun1();
function fun2() {}
fun2.prototype = objB;
let objC = new fun2();
alert(objC.prp); // 10 と表示される
objC が参照している fun2() が参照している objB こと fun1() のプロトコルこと objA までたどって、「 objA.prp = 10 」から、objC.prp を 10 とする評価を導き出しています。
INTERNOUS,inc. All rights reserved.