JavaScriptの__proto__とは何?

なぜ__proto__を理解するべきか

__proto__のコンセプトを理解することは、JavaScriptのオブジェクトとプロトタイプの関係性を深く理解するために重要です。以下に、その重要性をいくつかのポイントで説明します。

  1. プロトタイプベースの継承: JavaScriptはプロトタイプベースの言語であり、オブジェクト間での継承をプロトタイプチェーンを通じて実現します。__proto__を理解することで、オブジェクトがどのプロトタイプオブジェクトを参照しているかを把握し、継承関係を明確にすることができます。
  2. プロパティの検索とアクセス: オブジェクトが特定のプロパティにアクセスする際に、__proto__プロパティがプロトタイプチェーンの参照先を指し示します。プロパティの検索やアクセスが行われる際には、__proto__をたどって上位のプロトタイプオブジェクトまで遡ることになります。そのため、__proto__を理解することで、プロパティの参照や継承の仕組みを把握できます。
  3. オブジェクトの動的な変更: JavaScriptでは、__proto__を使用してオブジェクトのプロトタイプを動的に変更することができます。オブジェクトが参照するプロトタイプを変更することで、新たなメソッドやプロパティを継承させたり、既存の継承関係を変更することができます。
  4. プロトタイプの連鎖: __proto__を通じてプロトタイプオブジェクトが連鎖していくプロトタイプチェーンは、JavaScriptにおけるオブジェクトの継承メカニズムの基礎です。この概念を理解することで、より高度なオブジェクト指向のプログラミングやライブラリの使用が可能になります。
  5. パフォーマンスの最適化: プロトタイプチェーンはメモリ効率の観点からも重要です。オブジェクト間でプロトタイプを共有することで、同じメソッドやプロパティを複数のインスタンスで共有できます。これにより、メモリの使用量が削減され、パフォーマンスが向上します。

以上の理由から、__proto__のコンセプトを理解することは、JavaScriptのオブジェクト指向プログラミングやプロトタイプベースの継承を理解し、効果的に利用するために不可欠です。

__proto__とは

JavaScriptの__proto__は、オブジェクトのプロトタイプにアクセスするための特殊なプロパティです。プロトタイプは、別のオブジェクトから継承されたプロパティやメソッドのセットであり、他のオブジェクトとの関係性を定義します。

具体的には、あるオブジェクトの__proto__プロパティには、そのオブジェクトのプロトタイプオブジェクトへの参照が格納されます。プロトタイプオブジェクトは、そのオブジェクトが継承するプロパティやメソッドを持っており、__proto__を介してアクセスできます。

例えば、以下のようなコードを考えてみましょう:

const obj = { foo: 42 };
console.log(obj.__proto__); // デフォルトのプロトタイプオブジェクトであるObjectの参照が表示されます


objオブジェクトの__proto__プロパティには、Objectというデフォルトのプロトタイプオブジェクトへの参照が格納されています。そのため、objオブジェクトはObjectのプロパティやメソッドを継承して使用することができます。

ただし、__proto__はECMAScript 6で非推奨とされ、代わりにObject.getPrototypeOf()Object.setPrototypeOf()などのメソッドを使用することが推奨されています。

__proto__の例

もしJavaScriptで「Person」というオブジェクトを定義して、そのオブジェクトに「name」というプロパティと「greet」というメソッドを持たせたいと考えてみましょう。

const Person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};


ここで、新たに「Student」というオブジェクトを作成し、そのプロトタイプに「Person」オブジェクトを指定したい場合、__proto__を使用することができます。

const Student = {
  major: 'Computer Science'
};

Student.__proto__ = Person;


この例では、Student.__proto__を使ってStudentオブジェクトのプロトタイプをPersonオブジェクトに設定しています。これにより、StudentオブジェクトはPersonオブジェクトのプロパティとメソッドを継承します。

例えば、以下のようにgreet()メソッドを呼び出すことができます。

Student.greet(); // "Hello, my name is John." と出力されます


この例では、Studentオブジェクト自体にnameプロパティは存在しませんが、__proto__を介してPersonオブジェクトにアクセスし、nameプロパティを継承しています。そのため、greet()メソッド内で正しい名前が表示されることが確認できます。

ただし、注意点として、__proto__はECMAScript 6で非推奨とされており、代わりにObject.setPrototypeOf()メソッドを使用することが推奨されています。上記の例をObject.setPrototypeOf()を使って書き直すと、以下のようになります。

const Student = {
  major: 'Computer Science'
};

Object.setPrototypeOf(Student, Person);


Object.setPrototypeOf()を使用することで、同様のプロトタイプの設定が可能です。

プロトタイプチェーン

プロトタイプチェーン(Prototype Chain)は、JavaScriptのオブジェクト間でプロパティやメソッドの継承を実現する仕組みです。これにより、あるオブジェクトが他のオブジェクトのプロパティやメソッドを参照できるようになります。

JavaScriptでは、すべてのオブジェクトはプロトタイプ(prototype)と呼ばれる別のオブジェクトを持っています。プロトタイプオブジェクトは、継承元となるオブジェクトのプロパティやメソッドを格納しています。

オブジェクトがプロパティにアクセスするとき、まずそのオブジェクト自体にそのプロパティが存在するかどうかを確認します。もし存在しない場合、JavaScriptはオブジェクトの__proto__(またはObject.getPrototypeOf()メソッド)をたどって、次に検索するべきプロトタイプオブジェクトを見つけます。このプロセスは継続し、プロトタイプオブジェクトのプロパティを見つけるか、プロトタイプチェーンの終点であるObject.prototypeに到達するまで繰り返されます。

例えば、以下のようなコードを考えてみましょう:

const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

const student = {
  major: 'Computer Science'
};

Object.setPrototypeOf(student, person);

student.greet(); // "Hello, my name is John." と出力されます


上記の例では、personオブジェクトがstudentオブジェクトのプロトタイプとなっています。したがって、studentオブジェクトにgreet()メソッドが存在しない場合、プロトタイプチェーンを通じてpersonオブジェクトのgreet()メソッドが参照されます。

プロトタイプチェーンを適切に利用することで、オブジェクト間でのコードの再利用性や継承の実現が容易になります。ただし、プロトタイプチェーンは無制限に続くわけではなく、最終的にはObject.prototypeを終点としています。

setPrototypeOfとは

Object.setPrototypeOf()は、JavaScriptの組み込み関数の一つであり、オブジェクトのプロトタイプを設定するために使用されます。つまり、あるオブジェクトのプロトタイプを別のオブジェクトに変更することができます。

Object.setPrototypeOf()関数は以下のような構文を持ちます:

Object.setPrototypeOf(obj, prototype)
  • obj: プロトタイプを設定する対象のオブジェクト
  • prototype: 新しいプロトタイプとなるオブジェクト

例えば、次のようなコードを考えてみましょう:

const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

const student = {
  major: 'Computer Science'
};

Object.setPrototypeOf(student, person);

student.greet(); // "Hello, my name is John." と出力されます


上記の例では、Object.setPrototypeOf()を使用してstudentオブジェクトのプロトタイプをpersonオブジェクトに設定しています。その結果、studentオブジェクトはpersonオブジェクトのプロパティやメソッドを継承するようになります。したがって、student.greet()を呼び出すことでpersonオブジェクトのgreet()メソッドが実行され、適切なメッセージが表示されます。

Object.setPrototypeOf()はプロトタイプの変更を行うための便利なメソッドですが、プロトタイプチェーンを変更するために使用される場面は比較的少ないです。通常はオブジェクトを作成する際に適切なプロトタイプを設定することが推奨されます。また、Object.create()やクラス構文を使用してプロトタイプを指定する方法もあります。

__proto__には何が含まれている?

__proto__プロパティには、オブジェクトのプロトタイプオブジェクトへの参照が格納されます。プロトタイプオブジェクト自体がさらに他のプロパティやメソッドを持っている場合、それらも__proto__プロパティを介してアクセスできます。

具体的には、__proto__プロパティに含まれる内容は、次のようになります。

  1. プロパティ: プロトタイプオブジェクトが持つプロパティが__proto__を介して参照できます。これには、継承元のオブジェクトのプロパティや、その継承元のプロトタイプオブジェクトのプロパティが含まれます。
  2. メソッド: プロトタイプオブジェクトが持つメソッドも__proto__を通じてアクセスできます。これにより、継承元のオブジェクトのメソッドや、その継承元のプロトタイプオブジェクトのメソッドを呼び出すことができます。

以下の例を見てみましょう:

const person = {
  name: 'John',
  greet() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

const student = {
  major: 'Computer Science'
};

Object.setPrototypeOf(student, person);

console.log(student.__proto__); // { name: 'John', greet: [Function: greet] } と出力されます
console.log(student.__proto__.__proto__); // {} と出力されます


上記の例では、studentオブジェクトの__proto__プロパティはpersonオブジェクトを参照しています。したがって、student.__proto__personオブジェクトのプロパティとメソッドを含むオブジェクトを返します。さらに、personオブジェクト自体はObject.prototypeをプロトタイプとして持っているため、student.__proto__.__proto__は空のオブジェクトを返します。

__proto__はなぜES6から非推奨になったのか

__proto__は、ES6以前のJavaScriptにおいてプロトタイプの設定や参照に使用されていましたが、ES6でObject.setPrototypeOf()Object.getPrototypeOf()といったメソッドが導入され、より直感的かつ安全なプロトタイプの操作が可能になったため、__proto__は非推奨となりました。

__proto__が非推奨とされる理由は以下の通りです:

  1. 安全性の問題: __proto__は非標準のプロパティであり、直接アクセス可能です。これにより、意図しないプロトタイプの変更やプロトタイプチェーンの書き換えが発生する可能性があります。__proto__を誤って操作すると、意図しない結果や予測不可能なバグを引き起こす可能性があります。
  2. パフォーマンスの低下: __proto__を使用すると、オブジェクトのプロトタイプへの参照をたどるために、パフォーマンスが低下する可能性があります。一方、Object.setPrototypeOf()Object.getPrototypeOf()メソッドを使用すると、プロトタイプの参照や設定が最適化され、より効率的なプロトタイプ操作が可能です。
  3. 標準化の欠如: __proto__はECMAScriptの公式仕様ではなく、JavaScriptエンジンに依存する非標準のプロパティです。したがって、すべての環境で一貫した動作を保証することが難しくなります。一方、Object.setPrototypeOf()Object.getPrototypeOf()はECMAScriptの標準仕様であり、広くサポートされているため、互換性の問題が少なくなります。

これらの理由から、__proto__はES6以降のJavaScriptでは非推奨とされ、代わりにObject.setPrototypeOf()Object.getPrototypeOf()などのメソッドを使用することが推奨されています。これらのメソッドはより安全でパフォーマンスの良いプロトタイプ操作を提供し、コードの互換性とメンテナンス性を向上させることができます。

以上、__protp__の説明でした。

お疲れ様でした。