ここまで理解しているなら、プロトタイプと継承の「中の仕組み」や「落とし穴」を丁寧に掘り下げていける段階。
初心者でも「仕組みを感覚でつかめる」ように、図解イメージ+コード実験付きで説明していく。
ステップ1:プロトタイプチェーンの「中身」を見てみよう
まずは図でイメージ。
alice → Person.prototype → Object.prototype → null
これが「プロトタイプチェーン」。
aliceにはsayNameがない- →
alice.__proto__(つまりPerson.prototype)を探す - → なければさらに
Object.prototype - → 最後は
nullで終了
実際に確認できる。
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
const alice = new Person("Alice");
console.log(Object.getPrototypeOf(alice) === Person.prototype); // true
console.log(Object.getPrototypeOf(Person.prototype) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
JavaScriptつまり、JavaScript の「継承」はオブジェクトをチェーン状につなぐ探索ルールなんだ。
ステップ2:プロパティ探索のしくみ
オブジェクトにプロパティをアクセスすると、エンジンは次の順で探す:
- そのオブジェクト自身(自身のプロパティ)
- プロトタイプ(
__proto__) - さらにそのプロトタイプのプロトタイプ…
nullに行き着いたら探索終了(undefinedを返す)
例:
const animal = { eats: true };
const dog = Object.create(animal);
dog.barks = true;
console.log(dog.barks); // 1. 自身にある → true
console.log(dog.eats); // 2. 自身に無い → animal から借りる → true
console.log(dog.toString); // 3. Object.prototype から借りる
JavaScriptステップ3:プロパティの「設定(書き込み)」はどう動く?
読み取り(obj.prop)はチェーンを辿るけど、
書き込み(obj.prop = ...)は常に自分自身に新しいプロパティを作る。
const parent = { value: 10 };
const child = Object.create(parent);
child.value = 20;
console.log(child.value); // 20(自分のプロパティが新しくできた)
console.log(parent.value); // 10(親は変わらない)
JavaScriptこれが「シャドウイング(shadowing)」と呼ばれる動作。
つまり「見かけ上、親を上書きしたように見えて実は別物」。
ステップ4:プロパティの「性質」(ディスクリプタ)
プロパティには値だけでなく「メタ情報」がある。
それが プロパティディスクリプタ (Property Descriptor)。
主な属性:
| 属性 | 意味 | デフォルト値 |
|---|---|---|
value | 実際の値 | undefined |
writable | 値を書き換え可能か | false(define時はfalse、リテラル定義ではtrue) |
enumerable | for…in や Object.keys に出てくるか | true |
configurable | delete や再定義が可能か | true |
例で確認:
const obj = { a: 1 };
console.log(Object.getOwnPropertyDescriptor(obj, "a"));
JavaScript出力(例):
{
value: 1,
writable: true,
enumerable: true,
configurable: true
}
JavaScriptこれを使うと、プロパティの動作を細かく制御できる。
ステップ5:defineProperty で特殊なプロパティを作る
const obj = {};
Object.defineProperty(obj, "secret", {
value: "hidden",
writable: false, // 書き換え禁止
enumerable: false, // for...in に出ない
configurable: false // 削除不可
});
console.log(obj.secret); // "hidden"
obj.secret = "changed";
console.log(obj.secret); // 書き換えられないので "hidden" のまま
JavaScriptステップ6:プロトタイプの変更
✅ 確認する
Object.getPrototypeOf(obj);
JavaScript✅ 変更する
Object.setPrototypeOf(obj, newProto);
JavaScriptただし ⚠️注意:
- 実行時にプロトタイプチェーンを動的に変えると パフォーマンスが落ちる(エンジンの最適化が効かなくなる)。
- 普通は最初から
Object.create(proto)で設計時に決めておく方が安全。
ステップ7:クラス構文の裏側で起きていること
class Person {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}
const alice = new Person("Alice");
JavaScriptこれは実際には次のように展開される。
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
JavaScriptだから Person.prototype にメソッドを追加すれば、クラスでも同じように全インスタンスが共有する。
ステップ8:バグにつながりやすいケース(実例)
ケース1:共有オブジェクトを prototype に置く
function User(name) {
this.name = name;
}
User.prototype.data = [];
const u1 = new User("A");
const u2 = new User("B");
u1.data.push("X");
console.log(u2.data); // ["X"] 😱 全員で同じ配列を共有!
JavaScript👉 対策:
コンストラクタで新しい配列を作るようにする:
function User(name) {
this.name = name;
this.data = []; // 個別に持つ
}
JavaScriptケース2:this が変わる
👉 対策:
const greetFn = user.greet.bind(user);
greetFn(); // "Alice"
JavaScriptテップ9:安全に継承するパターン(中級向け)
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
function Dog(name) {
Animal.call(this, name); // 親のコンストラクタ呼び出し
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.speak = function() {
console.log(`${this.name} barks`);
};
const dog = new Dog("Max");
dog.speak(); // Max barks
JavaScriptES6 クラスでは👇と同じ意味になる:
class Animal {
constructor(name) { this.name = name; }
speak() { console.log(`${this.name} makes a sound`); }
}
class Dog extends Animal {
speak() { console.log(`${this.name} barks`); }
}
JavaScriptまとめ
| 概念 | 役割 |
|---|---|
prototype | コンストラクタ関数が持つ設計図(インスタンスが参照する) |
[[Prototype]](__proto__) | 実際の「親オブジェクト」へのリンク |
Object.create(proto) | 特定の親を持つオブジェクトを作る |
Object.getPrototypeOf() / setPrototypeOf() | プロトタイプの確認・変更 |
defineProperty() | プロパティの動作を細かく設定 |
継承 (extends) | クラスでプロトタイプチェーンを簡単に構築する方法 |

