JavaScriptにおけるプロトタイプと継承について徹底解説

プロトタイプ継承は、JavaScriptにおいてオブジェクト間の継承を実現する仕組みです。あるオブジェクトが別のオブジェクトを参照し、プロトタイプのプロパティやメソッドを自動的に継承します。これにより、コードの再利用性や拡張性を高めることができます。

では詳しく見ていきましょう。

プロトタイプを例に例えると

プロトタイプを身近なものに例えると、料理のレシピや調理手順の書かれた本と似ています。レシピ本は、様々な料理を作る際の手順や材料の情報を提供します。これをプロトタイプと考えると、実際の料理はオブジェクトであり、レシピ本がそのプロトタイプです。

例えば、あなたが特定のレシピ本を持っていて、そのレシピ本にはチョコレートケーキの作り方が書かれています。新しいケーキを作るとき、そのケーキはチョコレートケーキのレシピ本を参照し、手順や材料を継承します。レシピ本には他のケーキのレシピも含まれているかもしれませんが、チョコレートケーキを作る場合には、そのレシピだけを参照します。

同様に、JavaScriptのオブジェクトもプロトタイプを参照し、プロトタイプのプロパティやメソッドを継承します。プロトタイプは共通の情報や機能を提供し、オブジェクトはそれを利用して特定の目的や動作を実現します。

プロトタイプとはなにか

プロトタイプは、JavaScriptにおいてオブジェクト間の継承を実現するための仕組みです。プロトタイプとは、あるオブジェクトが別のオブジェクトを参照するプロパティのことを指します。

オブジェクトはプロパティとメソッドを持っており、それらの定義はオブジェクト自体に直接格納されます。しかし、オブジェクトが持つプロパティやメソッドを見つけるために、JavaScriptはまずオブジェクト自身を調べます。もし見つからない場合、JavaScriptはオブジェクトのプロトタイプを調べ、そこからプロパティやメソッドを探します。このプロトタイプチェーンをたどって、最終的に目的のプロパティやメソッドを見つけることができるのです。

プロトタイプは、新しいオブジェクトを作成する際に役立ちます。新しいオブジェクトを作成するとき、それを作成したオブジェクトのプロトタイプを指定することができます。これにより、新しいオブジェクトはプロトタイプのプロパティやメソッドを自動的に継承します。

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

// 親オブジェクト
var parent = {
  name: "親",
  sayHello: function() {
    console.log("Hello, " + this.name);
  }
};

// 子オブジェクト
var child = Object.create(parent);
child.name = "子";
child.sayHello(); // 出力: "Hello, 子"


この例では、parentオブジェクトが親となり、childオブジェクトがそのプロトタイプとして指定されています。childオブジェクトはparentオブジェクトのプロパティとメソッドを継承し、sayHelloメソッドを呼び出すことができます。

プロトタイプは、オブジェクトの階層構造を作成し、継承を実現するための重要な概念です。プロトタイプを活用することで、コードの再利用性や拡張性を高めることができます。初心者の方にとっては最初は少し難しく感じるかもしれませんが、プロトタイプの理解はJavaScriptの基礎を理解する上で重要なステップです。

プロトタイプの種類は?

JavaScriptにおいて、プロトタイプの種類は大きく分けて2つあります。

  1. オブジェクトのプロトタイプ(Object Prototype): すべてのオブジェクトは、オブジェクトのプロトタイプを持っています。これは、Object.prototypeという特別なオブジェクトです。すべてのオブジェクトは、Object.prototypeを暗黙的に参照し、そのプロパティやメソッドを継承します。
  2. カスタムプロトタイプ(Custom Prototype): 開発者が独自に作成したオブジェクトや関数には、そのカスタムプロトタイプが存在します。これは、新しいオブジェクトを作成する際に継承元として指定されるオブジェクトです。カスタムプロトタイプは、Object.prototypeを継承してさらに拡張することができます。

このように、オブジェクトのプロトタイプとカスタムプロトタイプの2つのプロトタイプがあります。それぞれのオブジェクトは、これらのプロトタイプを参照することで、継承やメソッドの呼び出し、プロパティのアクセスなどを行います。

Object Prototypeの中身は?

Object.prototypeは、すべてのJavaScriptオブジェクトが継承するプロトタイプオブジェクトです。以下に、Object.prototypeが持ついくつかの主要なプロパティとメソッドを示します:

  1. constructor: Objectコンストラクタ関数への参照です。すべてのJavaScriptオブジェクトは、このコンストラクタ関数によって作成されました。
  2. toString(): オブジェクトを文字列に変換するためのメソッドです。デフォルトの実装では、オブジェクトの型名を返します。
  3. hasOwnProperty(propertyName): オブジェクト自身が指定したプロパティを持つかどうかを判定するメソッドです。
  4. isPrototypeOf(object): あるオブジェクトが別のオブジェクトのプロトタイプチェーン内にあるかどうかを判定するメソッドです。
  5. valueOf(): オブジェクトをプリミティブな値に変換するためのメソッドです。通常、オブジェクト自体ではなく、オブジェクトが表す値を返します。

これらはいくつかの主要なプロパティとメソッドですが、Object.prototypeは他にもさまざまなプロパティやメソッドを持っています。JavaScriptのオブジェクトは、必要な場合にはObject.prototypeからこれらのプロパティやメソッドを継承し、利用することができます。

プロトタイプを継承している親のオブジェクトを変更する

プロトタイプを継承している親のオブジェクトを変更すると、それを継承している子のオブジェクトにも影響が及びます。

具体的には、子のオブジェクトは親のオブジェクトのプロパティやメソッドを継承しています。もし親のオブジェクトのプロパティやメソッドが変更されると、子のオブジェクトもそれを反映します。これは、プロトタイプベースの継承の仕組みによるものです。

例を見てみましょう:

// 親オブジェクトの定義
const parent = {
  property: 'value'
};

// 子オブジェクトの作成
const child = Object.create(parent);

console.log(child.property); // 出力: "value"

// 親オブジェクトのプロパティの変更
parent.property = 'new value';

console.log(child.property); // 出力: "new value"


上記の例では、parentオブジェクトを定義し、それを継承してchildオブジェクトを作成しています。childオブジェクトはparentオブジェクトのプロパティを継承しており、child.propertyとしてアクセスすることができます。

その後、parentオブジェクトのpropertyプロパティが変更されます。この変更は、childオブジェクトにも反映され、child.propertyを参照すると新しい値が表示されます。

このように、プロトタイプ継承では親のオブジェクトの変更が子のオブジェクトにも影響を与えます。プロトタイプチェーンを通じてプロパティやメソッドが参照されるため、親オブジェクトが変更されるとそれを継承している子オブジェクトも変更が反映される特性があります。

コンストラクタ関数とは

constructor function(コンストラクタ関数)は、JavaScriptにおいてオブジェクトを作成するための特別な関数です。コンストラクタ関数は、その名前が示すように、オブジェクトの初期化(構築)を担当します。

コンストラクタ関数は、通常大文字で始まる名前を持ち、newキーワードを使って呼び出されます。コンストラクタ関数を呼び出すと、新しいオブジェクトが作成され、そのオブジェクトのthisキーワードがコンストラクタ関数のインスタンスにバインドされます。また、コンストラクタ関数内では、thisを通じて新しいオブジェクトにプロパティやメソッドを追加することができます。

以下は、コンストラクタ関数の例です:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

const john = new Person('John', 25);
console.log(john.name); // 出力: "John"
console.log(john.age); // 出力: 25


上記の例では、Personというコンストラクタ関数が定義されています。このコンストラクタ関数は、nameageという引数を受け取り、新しいオブジェクトに対してそれぞれの値を設定します。

newキーワードを使ってPersonコンストラクタ関数を呼び出すと、新しいオブジェクトが作成されます。このオブジェクトはjohnという変数に割り当てられ、nameプロパティとageプロパティを持つようになります。

コンストラクタ関数は、同じ構造のオブジェクトを複数作成する際に便利です。また、コンストラクタ関数を使うことで、作成されたオブジェクトの状態を初期化することができます。

Property(プロパティ)とMethod(メソッド)

Property(プロパティ)とMethod(メソッド)は、オブジェクト指向プログラミングにおいて重要な概念です。以下にそれぞれの違いを詳しく説明します。

プロパティ(Property): プロパティは、オブジェクトが持つデータや値を表現します。オブジェクトの特徴や状態を示す変数のようなものです。プロパティは、オブジェクト内でキーと値のペアとして定義されます。プロパティには、数値、文字列、真偽値、関数、配列など、さまざまなデータ型の値を持つことができます。

例えば、以下のようなオブジェクトにおいて、nameageがプロパティです:

const person = {
  name: "John",
  age: 25
};


プロパティは、オブジェクトに関連付けられたデータを取得したり設定したりするために使用されます。プロパティへのアクセスは、ドット記法(object.property)またはブラケット記法(object["property"])を使用して行われます。

メソッド(Method): メソッドは、オブジェクトに紐付いた関数のことを指します。メソッドは、オブジェクトが実行できる操作や機能を表現します。メソッドは、オブジェクト内で関数として定義されます。

例えば、以下のようなオブジェクトにおいて、greetがメソッドです:

const person = {
  name: "John",
  age: 25,
  greet: function() {
    console.log("Hello, " + this.name + "!");
  }
};


メソッドは、オブジェクトに関連付けられた特定のアクションを実行します。他のプロパティやメソッドにアクセスするために、thisキーワードを使用することが一般的です。

メソッドは、オブジェクトの状態を変更するための処理や、オブジェクトが持つデータを操作するための処理などを行います。

プロパティとメソッドは、オブジェクト指向プログラミングにおいてオブジェクトの振る舞いや特性を表現するために使用されます。プロパティはデータを保持し、メソッドはそのデータに対する操作や処理を実行します。

プロトタイプ継承の方法

以下に、new演算子、Object.createメソッド、およびObject.setPrototypeOfメソッドを使用したプロトタイプ継承の例と説明を示します。

1. new演算子を使用したプロトタイプ継承:

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

Parent.prototype.greet = function() {
  console.log('Hello, ' + this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const child = new Child('John', 10);
child.greet(); // 出力: "Hello, John"


この例では、ParentChildという2つの関数を定義しています。Parent関数は親クラス(スーパークラス)を表し、Child関数は子クラス(サブクラス)を表します。

Child関数内で、Parent.call(this, name)を使用して親クラスのコンストラクタを呼び出し、子クラスのインスタンスに親クラスのプロパティを設定します。その後、Child.prototypeParent.prototypeを継承する新しいオブジェクトに置き換えます。これにより、ChildのインスタンスはParentのメソッドを継承します。

2. Object.createメソッドを使用したプロトタイプ継承:

const parent = {
  greet: function() {
    console.log('Hello, ' + this.name);
  }
};

const child = Object.create(parent);
child.name = 'John';
child.greet(); // 出力: "Hello, John"


この例では、parentというオブジェクトを定義し、greetというメソッドを持たせます。その後、Object.createメソッドを使用してparentオブジェクトを継承したchildオブジェクトを作成します。

childオブジェクトはparentオブジェクトをプロトタイプとして持ち、nameプロパティを追加します。childオブジェクトのgreetメソッドは、parentオブジェクトから継承されたメソッドを使用しています。

3. Object.setPrototypeOfメソッドを使用したプロトタイプ継承:

const parent = {
  greet: function() {
    console.log('Hello, ' + this.name);
  }
};

const child = {
  name: 'John'
};

Object.setPrototypeOf(child, parent);

child.greet(); // 出力: "Hello, John"


この例では、parentというオブジェクトとchildというオブジェクトを定義します。

今日はここまで!お疲れ様でした。