Skip to content

プロトタイプと継承の深堀り

公開日:November 16, 2024更新日:November 28, 2024
JavaScriptTypeScriptCoding📄

JavaScriptはオブジェクト指向のプログラミング言語であり、その中核にはプロトタイプベースの継承という特有の仕組みがあります。ここでは、JavaScriptのプロトタイプチェーンの詳細を理解し、クラス構文の内部で何が起きているのか、またオブジェクト指向プログラミング(OOP)をどのようにJavaScriptで実装するかを詳しく見ていきます。

プロトタイプチェーンとは

JavaScriptでは、すべてのオブジェクトは他のオブジェクトからプロパティやメソッドを継承することができます。これをプロトタイプチェーンと呼びます。

  • プロトタイプオブジェクト: JavaScriptのオブジェクトは、内部的に[[Prototype]]という隠しプロパティを持ちます。このプロパティは他のオブジェクトへの参照であり、オブジェクトが持っていないプロパティやメソッドを参照するために使われます。
  • プロトタイプチェーンの動作: プロパティやメソッドをオブジェクトに対して呼び出すと、JavaScriptエンジンはまずそのオブジェクト自身をチェックし、存在しない場合は[[Prototype]]を辿って親オブジェクトを探します。このチェーンがどんどん続いていき、最終的にnullに達するまで探索が続きます。これがプロトタイプチェーンです。

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

javascript
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name}`);
};

const alice = new Person('Alice');
alice.greet(); // "Hello, my name is Alice"

ここで、aliceオブジェクトにはgreetメソッドがありません。しかし、Person.prototypeに定義されたgreetメソッドをプロトタイプチェーンを通じて利用しています。

クラス構文と内部の挙動

ES6で導入されたクラス構文は、JavaScriptでオブジェクト指向プログラミングをより扱いやすくするための糖衣構文です。クラスは、見た目が他の言語(JavaやC++など)のクラスと似ていますが、実際にはプロトタイプベースの仕組みの上に構築されています。

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

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

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

const dog = new Dog('Rex');
dog.speak(); // "Rex barks."
  • クラスとプロトタイプ: 上記のコードでclass Animalを定義していますが、内部的にはコンストラクタ関数として動作しています。speakメソッドはAnimal.prototypeに追加されており、継承するDogクラスはAnimalを拡張することでプロトタイプチェーンを利用しています。
  • 継承の仕組み: DogクラスはAnimalクラスをextendsしており、superキーワードを使うことで親クラスのメソッドやプロパティにアクセスできます。このように、JavaScriptのクラスはプロトタイプチェーンをベースにして継承を実現しています。

オブジェクト指向プログラミングのパターン

JavaScriptでは、さまざまなオブジェクト指向プログラミングのパターンを実装できます。以下はいくつかの代表的なパターンです。

  1. コンストラクタパターン: コンストラクタ関数を用いてオブジェクトを生成し、そのプロトタイプにメソッドを追加する方法です。プロトタイプチェーンを使って効率的にメモリを節約し、共通のメソッドをすべてのインスタンスで共有します。

    javascript
    function Car(make, model) {
      this.make = make;
      this.model = model;
    }
    
    Car.prototype.start = function() {
      console.log(`${this.make} ${this.model} is starting.`);
    };
    
    const myCar = new Car('Toyota', 'Corolla');
    myCar.start();
  2. モジュールパターン: JavaScriptでは、モジュールパターンを使ってデータの隠蔽と名前空間を管理できます。IIFE(即時実行関数)を使って、プライベートなスコープを持つオブジェクトを作成することが一般的です。

    javascript
    const Calculator = (function() {
      let result = 0;
    
      return {
        add: function(x) {
          result += x;
        },
        getResult: function() {
          return result;
        }
      };
    })();
    
    Calculator.add(5);
    console.log(Calculator.getResult()); // 5
  3. シングルトンパターン: シングルトンは、特定のクラスが一つのインスタンスしか持たないことを保証するパターンです。例えば、アプリケーション全体で一つだけ存在する設定管理オブジェクトなどに使われます。

    javascript
    const Singleton = (function() {
      let instance;
    
      function createInstance() {
        return { name: 'I am the instance' };
      }
    
      return {
        getInstance: function() {
          if (!instance) {
            instance = createInstance();
          }
          return instance;
        }
      };
    })();
    
    const instance1 = Singleton.getInstance();
    const instance2 = Singleton.getInstance();
    console.log(instance1 === instance2); // true

まとめ

JavaScriptのプロトタイプベースの継承は、クラスベースの他の言語とは異なる独特な仕組みです。class構文は、より直感的にオブジェクト指向プログラミングを行うための表現方法ですが、内部ではプロトタイプチェーンが使われています。JavaScriptのプロトタイプと継承の理解を深めることで、オブジェクト指向プログラミングのさまざまなパターンを効果的に利用し、柔軟で再利用可能なコードを書くことが可能です。