2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのプロトタイプチェーン完全理解:継承構造・__proto__・constructorの正体を紐解く

Posted at

概要

JavaScriptは プロトタイプベースの継承モデル を採用しており、クラスベースとは異なるメカニズムでオブジェクト同士の関係性が構築される。
この関係を支えるのが「プロトタイプチェーン」であり、以下のような現象を支配している:

  • なぜオブジェクトに定義していない toString() が呼べるのか
  • 関数に自動的に prototype プロパティが存在する理由
  • __proto__prototype の違い
  • new キーワードが内部的に何をしているのか

この記事では、プロトタイプチェーンの構造・設計・使い所・落とし穴を徹底的に整理する。


プロトタイプベースの継承とは?

すべてのオブジェクトは、別のオブジェクト(プロトタイプ)を参照している。
プロパティやメソッドが見つからなかった場合、その参照先をたどって探索が行われる。
この参照の連鎖が「プロトタイプチェーン」。

const obj = {};
obj.toString(); // ✅ Object.prototype.toString が参照されている

基本構造

const a = {};
console.log(a.__proto__ === Object.prototype); // true
  • a 自体は toString を持たない
  • a.__proto__(非推奨だが慣例的に使用) を辿ると Object.prototype に到達
  • 最終的に Object.prototype.__proto__ === null → チェーンの終端

関数と prototype の関係

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return `${this.name} makes a sound`;
};

const dog = new Animal('Pochi');
dog.speak(); // 'Pochi makes a sound'

✅ 内部的な流れ

1. Animal.prototype が dog.__proto__ に代入される
2. dog.speak() → dog.__proto__.speak() を参照

__proto__prototype の違い

用語 意味
obj.__proto__ インスタンスが参照するプロトタイプ
Func.prototype 関数コンストラクタが作るインスタンスの参照先
obj.constructor インスタンスを生成した関数(Func)
dog.__proto__ === Animal.prototype;     // ✅ true
Animal.prototype.constructor === Animal; // ✅ true

new キーワードの内部的な動作

const d = new Animal('Toto');

内部的には以下の処理が行われている:

function newOperator(Cls, ...args) {
  const instance = Object.create(Cls.prototype);
  Cls.apply(instance, args);
  return instance;
}

→ ✅ Object.create() によりプロトタイプを接続
→ ✅ apply によりインスタンスの初期化


プロトタイプチェーンの視覚イメージ

dog --> Animal.prototype --> Object.prototype --> null

継承の拡張:プロトタイプによる多段階継承

function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.speak = function () {
  return `${this.name} barks`;
};

const d = new Dog('Pochi');
d.speak(); // Pochi barks

クラス構文との接続点(ES6以降)

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name} makes a sound`;
  }
}

class Dog extends Animal {
  speak() {
    return `${this.name} barks`;
  }
}

const d = new Dog('Toto');
d.speak(); // Toto barks

→ ✅ この構文は糖衣構文であり、内部ではプロトタイプチェーンが構築されている


よくある誤解

prototype はインスタンスに存在しない

console.log(d.prototype); // undefined

→ ✅ prototype関数にしか存在しないプロパティ


__proto__ は非標準(とはいえ使われがち)

→ ✅ 安全には Object.getPrototypeOf(obj) を使う


設計におけるプロトタイプの使い所

目的 適切な設計手段
複数のインスタンスで同一メソッドを共有したい Func.prototype.method = ...
クラスベースの継承を書きたい ES6 class / extends 構文
プロトタイプチェーンを明示制御したい Object.create() / setPrototypeOf()
高速な共有メソッド設計 prototype ベースの設計

結語

JavaScriptのオブジェクト設計は、「クラスベースの模倣」ではない。
それはプロトタイプという連鎖構造により、オブジェクト間の振る舞いを合成する設計である。

  • __proto__ は参照先
  • prototype は生成元の青写真
  • new は両者をつなぐトリガー

構造を理解すれば、継承の挙動は“必然的”に説明できるようになる。
クラス構文に頼る前に、プロトタイプという“原点”を設計として理解せよ。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?