吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】オブジェクトについて Vol.3(プロトタイプオブジェクト)

2018年1月20日 / category : javascript

今回の記事では、オブジェクトについて解説しながら、JavaScriptがプロトタイプベースのオブジェクト指向であるとはどういうことかについて触れていければと思います。

プロトタイプオブジェクトについて

早速、次のコードを見てみます。

sample.js
function Test(){};
let t = new Test();
console.log( t ); // Test {}

変数tはTestオブジェクトのインスタンスになります。Testオブジェクトはプロパティを持たない空のオブジェクトに見えますが、コンソール画面に表示された{}を展開すると次のようになります。

Test {}
 __proto__: Object

Testオブジェクトのインスタンスには、__proto__というプロパティが存在します。

sample.js
function Test(){};
let t = new Test();
console.log( t.__proto__ ); // {constructor: ƒ,...}
console.log( typeof t.__proto__ ); // object

プロパティ__proto__には、オブジェクトが参照されていることがわかります。実はこの参照されているオブジェクトが、このオブジェクト(インスタンス)が生成される際の取得元(継承元)となるテンプレート(オブジェクト)で、このテンプレートをプロトタイプオブジェクトと呼びます。
この関係を図で表すと次のようになります。

この図を見ると、インスタンスが生成されると自身のオブジェクト(変数tがオブジェクト)と、プロトタイプのオブジェクトの2つのオブジェクトで形成されていることがわかります。また、プロトタイプは取得元(継承元)となるテンプレート(オブジェクト)ですので、この場合、Testオブジェクトが参照されることになりますが、Testオブジェクト自身ではなく、Testオブジェクトのプロトタイプが参照されることになります。
ここまでを図にすると、次のようになります。

テンプレート(オブジェクト)は定義された際に自身のプロトタイプをプロパティとして保持します。

sample.js
function Test(){};
console.log( Test.prototype ); // {constructor: ƒ,...}

上のコードを見ると、プロパティprototypeにオブジェクト(プロトタイプオブジェクト)が参照されていることがわかります。
また、先ほどのコードで生成したインスタンスのプロパティ__proto__と、Testオブジェクトのプロパティprototypeが参照するプロトタイプオブジェクトが同じだということもわかります。

sample.js
function Test(){};
let t = new Test();
console.log( Test.prototype === t.__proto__ ); // true

では、このプロトタイプオブジェクトとはどんなオブジェクトでしょうか。
コンソールで確認してみます。

sample.js
function Test(){};
console.log( Test.prototype ); // {constructor: ƒ}

コンソールで出力された値を展開してみます。

{constructor: ƒ}
	constructor:ƒ Test()
	__proto__:Object

プロトタイプオブジェクトには、プロパティconstructorと、__proto__があることがわかりました。プロパティconstructorについては後ほど解説します。プロトタイプオブジェクトの中にさらにプロパティ__proto__があるということは、このプロトタイプオブジェクトはさらにテンプレートとなるオブジェクトが存在することを示します。このオブジェクトが、Objectオブジェクトのプロトタイプオブジェクトになります。

sample.js
function Test(){};
console.log( Test.prototype.__proto__ === Object.prototype ); // true

ここまでを図にしてみます。

Testオブジェクトのインスタンスのプロトタイプを辿っていくとObjectオブジェクトのプロトタイプまで辿り着きました。では、Objectオブジェクトのプロトタイプのテンプレートにはどんなオブジェクトが参照されているでしょうか。

sample.js
console.log( Object.prototype.__proto__ ); // null

nullが参照されました。どうやらここが最終地点のようです。

続いて、Testオブジェクトに再度目を向けてみます。Testオブジェクトは関数です。関数は、Functionオブジェクトになります。

sample.js
function Test(){};
console.log( typeof Test ); // function
console.log( Test instanceof Function ); // true

また、上のコードでわかるようにTestオブジェクトはFunctionオブジェクトのインスタンスということも確認できます。インスタンスということは、プロパティ__proto__を保持しています。Functionオブジェクトのインスタンスなので、プロパティ__proto__は、Functionオブジェクトのプロトタイプを参照します。

sample.js
function Test(){};
console.log( Test.__proto__ === Function.prototype ); // true

ここまでを図に整理します。

続いて、Functionオブジェクトのプロトタイプのテンプレートが何を参照しているか確認します。

sample.js
console.log( Function.prototype.__proto__ ); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
console.log( Function.prototype.__proto__ === Object.prototype ); // true

Testオブジェクトにプロパティ__proto__があるように、Functionオブジェクト、Objectオブジェクトにもプロパティ__proto__でそれぞれプロトタイプを参照しています。こちらも確認してみます。

sample.js
console.log( Function.__proto__ ); // ƒ () { [native code] }
console.log( Function.__proto__ === Function.prototype ); // true
console.log( Object.__proto__ ); // ƒ () { [native code] }
console.log( Object.__proto__ === Function.prototype ); // true

上の結果からFunctionオブジェクトのプロトタイプは、Functionオブジェクトのプロトタイプを参照しています。このことから言い方を少し変えて、関数オブジェクトのプロトタイプの最終地点はFunctionオブジェクトのプロトタイプになるということがわかりました。
また、ObjectオブジェクトのプロトタイプもFunctionオブジェクトのプロトタイプを参照しています。
これらを先ほどの図にまとめてみます。

以上がプロトタイプについてですが、ここまでをまとめると、次のことが言えます。

・オブジェクトには、プロトタイプとなるオブジェクトが存在する。
・全てのオブジェクトは、Objectオブジェクトのプロトタイプを継承している。

上の例では変数tに関数オブジェクトを代入していました。
関数オブジェクトではなく、次のようなオブジェクトの場合、プロトタイプはどうなるでしょうか。

sample.js
let t = {
	txt: 'お腹減った'
};
console.log( t instanceof Object ); // true
console.log( t.prototype ); // undefined
console.log( t.__proto__ ); // {constructor: ƒ,...}
console.log( t.__proto__ === Object.prototype ); // true

変数tには、プロパティtxtを保持したオブジェクトが代入されています。またObjectオブジェクトのインスタンスということも確認できます。自身にプロトタイプは持っていませんので、プロパティprototypeは保持しません。Objectオブジェクトのインスタンスなので、プロパティ__proto__で参照するプロトタイプはObjectオブジェクトのプロトタイプとなります。
次回もプロトタイプについて解説していきたいと思います。