全てのオブジェクトは prototype という名前のプロパティを持ちます。
これを prototype オブジェクト と呼びます。

プロトタイプ継承

プロパティの読み込みの優先順位を調べるために、
各段階でプロパティをセットしてみます。

まず Fn クラスを定義します。

function Fn(){
  this.fn1 = 'fn1@クラス';
  this.fn2 = 'fn2@クラス';
}

プロパティ fn1, fn2 を持たせています。
続いて、いま定義した Fn クラスのインスタンスを生成します。

// Fn コンストラクタでオブジェクト生成
var obj1 = new Fn(); 

準備が整ったので、
Object クラス、Fn クラス、インスタンス obj1 にプロパティをセットしていきます:

// Object クラスの prototype オブジェクトにプロパティをセット
Object.prototype.fn1 = 'fn1@オブジェクト';
Object.prototype.fn2 = 'fn2@オブジェクト';
Object.prototype.fn3 = 'fn3@オブジェクト';
Object.prototype.fn4 = 'fn4@オブジェクト';

// Fn クラスの prototype オブジェクトにプロパティをセット
Fn.prototype.fn1 = 'fn1@プロトタイプ';
Fn.prototype.fn2 = 'fn2@プロトタイプ';
Fn.prototype.fn3 = 'fn3@プロトタイプ';

// Fn クラスのインスタンス obj1 にプロパティをセット
obj1.fn1 = 'fn1@インスタンス';

では、プロパティの読み込みをしてみます:

document.writeln(obj1.fn1); // fn1@インスタンス
document.writeln(obj1.fn2); // fn2@クラス
document.writeln(obj1.fn3); // fn3@プロトタイプ
document.writeln(obj1.fn4); // fn4@オブジェクト

fn4, fn1 の結果はあたりまえです。

fn3 の結果から、
プロトタイプでのプロパティ定義が Object クラスでのプロパティ定義を上書いてしまったことがわかります:

 Object クラスでの定義 < プロトタイプでの定義

さらに、fn2 の結果から、

 プロトタイプでの定義 < クラスでの定義

ということが分かります。

以上からオブジェクトのプロパティ読み込みの優先順は、

  • インスタンスオブジェクト自身にセットされたプロパティ
  • クラスにセットされたプロパティ
  • クラスの prototype プロパティにセットされたプロパティ
  • 組み込みオブジェクトの Object クラスにセットされたプロパティ

の順番であることがわかりました。

このようにプロパティの読み込みを行う機能を「プロトタイプ継承」と呼び、この流れを プロトタイプチェーン と呼びます。

プロトタイプオブジェクト

Fn クラスのインスタンスオブジェクト obj を以下のように生成したとします:

function Fn(){
};
var obj = new Fn();

このとき、Fn.prototype が参照するオブジェクトをオブジェクト obj の プロトタイプオブジェクト と呼びます。
Fn のプロトタイプオブジェクトは Function.prototype が参照するオブジェクトです。

インスタンスオブジェクトの判定

インスタンスオブジェクトが何をプロトタイプ継承したか、
どんなプロパティを持つのか、を判定する方法:

function G(){
};
function H(){
  this.h = '1';
};
G.prototype = new H();
var a = new G();
a.x = 1;
a.y = 2;
 
// 以下の判定方法はプロトタイプチェーンを辿る
document.writeln(a.constructor); // function H(){};
document.writeln(a instanceof G); // true
document.writeln(a instanceof H); // true
document.writeln(G.prototype.isPrototypeOf(a)); // true
document.writeln(H.prototype.isPrototypeOf(a)); // true
document.writeln('x' in a); // true
document.writeln('y' in a); // true
document.writeln('h' in a); // true
 
// 以下の判定方法はプロトタイプチェーンを辿らない
document.writeln(Object.keys(a)); // x,y
document.writeln(Object.getOwnPropertyNames(a)); // x,y
var arr = [1, 2];
document.writeln(Object.keys(arr)); // 0,1
document.writeln(Object.getOwnPropertyNames(arr)); // 0,1,length

getOwnPropertyNames メソッドは enumerable 属性を無視するため、
enumerable 属性が偽の length プロパティも出力される。