6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年1月18日 JavaScript (JS Primer) プロトタイプチェーン

復習

プロトタイプメソッドはプロトタイプオブジェクトのプロパティとして定義しています。 クラスをインスタンス化したときにプロトタイプオブジェクトのプロパティとしてメソッドが組み込まれます。

class構文で定義したプロトタイプメソッドはプロトタイプオブジェクトに定義されます。 しかし、インスタンス(オブジェクト)にはメソッドが定義されていないのに、インスタンスからクラスのプロトタイプメソッドを呼び出せます。

class MyClass {
    method() {
        console.log("プロトタイプのメソッド");
    }
}
const instance = new MyClass();
instance.method(); // "プロトタイプのメソッド"

そもそもの話ですが、インスタンスオブジェクトにメソッドを定義していないのにインスタンスからプロトタイプメソッドを定義出来るのはなぜでしょうか?

それはプロトタイプチェーンという仕組みがあるからです。

プロトタイプチェーンとは2つの処理から成り立ちます。

インスタンス作成時にPrototype内部プロパティに保存し、インスタンスからプロパティ参照するときに探索してくれます。

実際に上記で生成したインスタンスを出力して検証してみました。

class MyClass {
    method() {
        console.log("プロトタイプのメソッド");
    }
}
const instance = new MyClass();
console.log(instance); // "プロトタイプのメソッド"
出力結果: {}

instanceオブジェクトの中身です。

クラスからnew演算子によってインスタンスを作成する際に、インスタンスにはクラスのプロトタイプオブジェクトへの参照が保存されます。 このとき、インスタンスからクラスのプロトタイプオブジェクトへの参照は、インスタンスオブジェクトの[[Prototype]]という内部プロパティに保存されます。

内部プロパティはECMAScriptの仕様で定められた内部的な表現であるため、通常のプロパティのようにはアクセスできません。 Object.getPrototypeOfメソッドで[[Prototype]]内部プロパティを参照できます。

以下は引用です。内部プロパティは読み書きすることは出来ますが、あまり推奨されていないようですね。 

Object.getPrototypeOf(オブジェクト)でオブジェクトのPrototypeを読み取ることができます。
一方、Object.setPrototypeOf(オブジェクト, プロトタイプオブジェクト)でオブジェクトのPrototypeにプロトタイプオブジェクトを設定できます。 また、Prototype内部プロパティを通常のプロパティのように扱えるprotoという特殊なアクセッサプロパティが存在します。

しかし、これらのPrototype内部プロパティを直接読み書きすることは通常の用途では行いません。 また、既存のビルトインオブジェクトの動作なども変更できるため、不用意に扱うべきではないでしょう。

インスタンスオブジェクトに定義したメソッドとプロトタイプメソッドを確認

class ConflictClass {
    constructor() {
        this.method = () => {
            console.log("インスタンスオブジェクトのメソッド");
        };
    }
    method() {
        console.log("プロトタイプメソッド");
    }
}
const conflict = new ConflictClass();
console.log(conflict);

出力結果: { method: [Function] }

インスタンスからプロトタイプメソッドが呼び出せるのは上の画像の[[Prototype]]method: f method() と保存されていて、こちらを探索しています。これがプロトタイプチェーンという仕組みです。

参考

JavaScript (JS Primer) プロトタイプチェーン