このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【JavaScript入門】オブジェクト指向

オブジェクト指向

 ここからは、JavaScriptのオブジェクト指向プログラミングについて学んでいきます。まず「オブジェクト指向とはなにか」という概念を説明し、次にJavaScriptでそれをどのように実現するのかを見ていきましょう。JavaScriptはプロトタイプベースの言語ですが、ES6(ECMAScript 2015)以降で導入された「class」構文などを用いると、オブジェクト指向の考え方をより直感的に扱うことができます。

 ここでは、「クラス(class)」「インスタンス(instance)」「継承(inheritance)」「多態性(polymorphism)」といったキーワードを取り上げながら解説していきます。サンプルコードを例示し、HTMLに組み込んだ具体例とその動作を説明します。

1.オブジェクト指向とは

 大規模化・複雑化したプログラムを効率よく開発し、管理するための方法論として登場したのがオブジェクト指向プログラミングです。データとそれを処理する関数を一つにまとめたものを「オブジェクト」と呼び、この仕組みによって、プログラムをモジュール化し、保守性を向上させることが可能になります。

1.1. カプセル化

 カプセル化とは、データやそれを操作する関数を一つのオブジェクトの中にまとめ、外部に公開する必要のあるものだけを公開し、残りを隠蔽する仕組みです。
 たとえば自動販売機では、お金を投入したり商品ボタンを押したりといった外部とのやり取りが決まっており、その内部の機械部分は隠されています。オブジェクト指向でも、オブジェクトの「外部に公開した部分」だけを使って機能を利用し、内部の実装は隠すことができます。

自動販売機オブジェクト
中の機械を隠蔽データを隠蔽
硬貨投入口や商品のボタンで操作を行う。外部に公開した関数で操作を行う。

1.2. クラスとインスタンス

 オブジェクト指向プログラミングにおいて、「クラス」はオブジェクトの設計図のような役割を持ちます。そしてクラスから具体的に生成されたものを「インスタンス」と呼びます。

自動販売機オブジェクト
機械の設計図クラス
機械を製造インスタンス化
実際の機械インスタンス

 設計図(クラス)の段階で振る舞いやデータを定義し、そこから必要な数だけインスタンスを作ることで、それぞれに独立したデータを持たせたり、同じ処理を行わせたりできます。

2.JavaScriptにおけるオブジェクト指向

 JavaScriptは厳密には「プロトタイプベースのオブジェクト指向言語」ですが、ES6以降、class 構文が導入され、よりクラスベースに近い書き方ができるようになりました。また、継承や静的メンバーといった概念もクラス構文で扱いやすくなっています。

2.1. 継承

 継承(inheritance)とは、あるクラス(親クラス)を元に新しいクラス(子クラス)を作る仕組みです。元のクラスの機能やデータを受け継ぎつつ、新たな機能を追加したり、一部を書き換えたりできます。

オブジェクト
元の車種親クラス
改良継承
新しい車種子クラス

 クラス同士を継承することで、親の特徴をベースにさらに機能を持つ子クラスを作り、プログラムを段階的に発展させることができます。

2.2. 多態性(ポリモーフィズム)

 多態性(polymorphism)とは、継承したオブジェクトが、同じ名前のメソッドを持ちながら、それぞれ違う挙動を実装できる性質です。これによって、「同じメソッド呼び出し」であっても、対象となるオブジェクトによって違った処理を行うことができます。

天井の電灯オブジェクト
蛍光灯親オブジェクト
スイッチを押す関数Aを実行
蛍光灯の明かりが点くある処理をおこなう
命令は同じで処理の中身が違う同じ命令で処理の中身が違う
LED子オブジェクト
スイッチを押す関数Aを実行
LEDの明かりが点く別の処理をおこなう

 親クラスで定義したメソッドを子クラスで上書き(オーバーライド)すると、それぞれに適した処理を実行できます。

3.サンプルプログラムの例

 ここからは、実際にJavaScriptのクラス構文を使った簡単なサンプルを示します。次のHTMLファイル(ファイル名: object_oriented_example.html)を用意してください。ブラウザでこのHTMLを開くと、JavaScriptによってコンソールや画面にメッセージが表示されます。

 ここでは、オブジェクト指向について「まずはこういうものなんだ」と感覚的に捉えて、気軽に試してみてください。
クラスの作り方については、このあと詳しく説明していきます。

【object_oriented_example.html】

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>オブジェクト指向サンプル</title>
</head>
<body>
  <h1>JavaScript オブジェクト指向のサンプル</h1>
  <p id="result"></p>

  <script>
    // クラス(設計図)を定義
    class VendingMachine {
      constructor(name) {
        this.name = name; // 自動販売機の名前
      }

      // ジュースを販売するメソッド
      dispense() {
        return this.name + "でジュースを購入しました。";
      }
    }

    // 親クラスを継承した子クラスを定義
    class SpecialVendingMachine extends VendingMachine {
      constructor(name, campaignMessage) {
        super(name); // 親クラスのコンストラクタを呼び出す
        this.campaignMessage = campaignMessage;
      }

      // 親クラスのdispenseをオーバーライド
      dispense() {
        // 親のdispense処理に加えて、キャンペーンのメッセージを返す
        return super.dispense() + " " + this.campaignMessage;
      }
    }

    // クラスをインスタンス化
    const machineA = new VendingMachine("標準機");
    const machineB = new SpecialVendingMachine("特別機", "キャンペーン中!");

    // コンソールに出力
    console.log(machineA.dispense());
    console.log(machineB.dispense());

    // 画面に出力
    document.getElementById("result").innerHTML 
      = machineA.dispense() + "<br>" + machineB.dispense();
  </script>
</body>
</html>

デバックコンソールの出力

標準機でジュースを購入しました。
特別機でジュースを購入しました。 キャンペーン中!

ブラウザで開くと、上記の内容がコンソールとWebページの両方に表示されます。

プログラム解説

  1. class VendingMachine
    constructor(name)で、生成される自動販売機オブジェクトに名前(this.name)を設定しています。
    dispense()メソッドでは、「〜でジュースを購入しました。」という文字列を返しています。
  2. class SpecialVendingMachine extends VendingMachine
    extends VendingMachineで、VendingMachineクラスを継承しています。
    ・子クラスであるSpecialVendingMachineconstructorでは、super(name)を使い、親クラスのコンストラクタを呼び出し、さらに独自のプロパティcampaignMessageを追加しています。
    dispense()メソッドをオーバーライドすることで、親クラスのdispense()の処理に加えてキャンペーンメッセージを返すようにしています。
  3. new VendingMachine("標準機")new SpecialVendingMachine("特別機", "キャンペーン中!")
    ・クラスを使ってオブジェクト(インスタンス)を生成しています。
    machineAは通常の自動販売機オブジェクト、machineBはキャンペーン付きの自動販売機オブジェクトです。
  4. console.log(...)document.getElementById("result").textContent = ...;
    ・コンソールとWebページ上に、2つのインスタンスが返すメッセージを表示しています。

4.実際に使うときの注意点

  • JavaScriptでクラスを定義するときは、内部のメソッドやプロパティをどこまで公開するか意識することで、カプセル化の考え方を活用できます(現状、完全なアクセス制御は工夫が必要ですが、ES2022で導入されたクラスのプライベートメンバー記法 # を利用する手段などもあります)。
  • 継承によって段階的に機能を拡張し、共通する部分を親クラスにまとめておくと、保守性が向上します。
  • 多態性を利用すると、「同じメソッド呼び出し」による異なる挙動を簡潔に実装できます。

まとめ

  • オブジェクト指向では、データと処理を一体化した「オブジェクト」に着目し、大規模プログラムの開発や保守を容易にします。
  • カプセル化でオブジェクト内部を隠蔽し、外部に公開する部分だけを限定することで、変更に強い設計が可能になります。
  • クラスとインスタンスの仕組みにより、設計図(クラス)をもとに独立した複数のインスタンスを生成できます。
  • 継承を使うと、親クラスの機能を継承した子クラスを作りやすくなります。
  • 多態性では、同じメソッド名を使いながら内部の処理を変えられるため、可読性と拡張性が高まります。

 JavaScriptでオブジェクト指向を活用することで、コードの再利用性や可読性が向上し、より大きなプロジェクトの開発にも対応しやすくなります。今回のサンプルプログラムを足がかりに、クラス構文やプロトタイプの仕組みを用いてオブジェクト指向プログラミングの手法を身につけていきましょう。