JavaScript のオブジェクトは、内部に [[Prototype]] という隠しリンクを持ち、プロパティ探索を上位オブジェクトへ委譲する仕組みを備えている。このメカニズムを正確に理解するには、メモリ上でどのように参照が張られるかをイメージすることが不可欠である。
メモリ上の構造
- 関数オブジェクト
Userが生成されると、同時にUser.prototypeという別オブジェクトが確保される。 new User()でインスタンスu1が作られると、u1内部の[[Prototype]]スロットがUser.prototypeを指す。- プロパティ参照時、インスタンス自身に該当キーがなければ
[[Prototype]]を辿って探索が継続される。
動作確認コード
function User() {}
// プロトタイプに共有プロパティを追加
User.prototype.handle = '匿名';
User.prototype.score = 0;
User.prototype.show = function () {
console.log(`${this.handle}: ${this.score}`);
};
const u1 = new User();
u1.show(); // 匿名: 0
// インスタンス固有の値を設定
u1.handle = 'タケシ';
u1.show(); // タケシ: 0
const u2 = new User();
u2.show(); // 匿名: 0 ← プロトタイプ値が参照される
u1.handle = 'タケシ' を実行した瞬間、u1 自身のプロパティとして新しい handle スロットが作成され、以降はプロトタイプの値は隠蔽される(プロパティシャドウイング)。
プロトタイプ診断メソッド
// プロトタイプチェーンに指定オブジェクトが含まれているか
console.log(User.prototype.isPrototypeOf(u1)); // true
// コンストラクタ参照
console.log(u1.constructor === User); // true
// 独自プロパティかどうか
console.log(u1.hasOwnProperty('handle')); // true
console.log(u2.hasOwnProperty('handle')); // false
// プロパティを削除してプロトタイプ値を再露出
delete u1.handle;
console.log(u1.hasOwnProperty('handle')); // false
console.log(u1.handle); // 匿名 ← プロトタイプ値が見える
// in 演算子はチェーン全体を探索
console.log('handle' in u1); // true
console.log('level' in u1); // false
このように、プロトタイプは単なる「デフォルト値の倉庫」ではなく、メモリ効率を保ちながらメソッドや不変データを共有するための基盤となっている。