基礎が片付いたところで、オブジェクト指向 JavaScript (OOJS) について取り上げます。この記事ではオブジェクト指向プログラミング (OOP) の基本的な視点を説明し、 JavaScript がどのようにコンストラクター関数を通じてオブジェクトクラスをエミュレートしているか、またどのようにオブジェクトインスタンスを生成しているかを紹介します。

前提知識: 基礎的なコンピューターリテラシー、基礎的な HTML と CSS の理解、 JavaScript (JavaScript の第一歩JavaScript の構成要素を参照)及び OOJS (JavaScript オブジェクトの基本を参照)の基礎知識。
到達目標: オブジェクト指向プログラミングの基本理論、どのようにそれが JavaScript (「すべてはオブジェクトである」)に関連しているか、どのようにコンストラクターがオブジェクトインスタンスを生成しているかを理解する。

オブジェクト指向プログラミング - その基本

はじめに、オブジェクト指向プログラミング (OOP) とは何か、シンプルかつ高レベルな視点を提示します。シンプルと述べたのは、OOP はあっという間にひどく複雑になり得るためで、現時点でそのすべてを論じてしまうと、助けとなるよりもむしろ混乱を生んでしまうことでしょう。OOP の基本的な考え方は、プログラムの中で扱いたい、現実世界の事物を模るためにオブジェクトを使用すること、またはそうしなければ使うことが難しいあるいは不可能だった機能にアクセスするための、シンプルな方法を提供することです。

オブジェクトは、モデル化しようとしている事物に関する情報及び、持たせたい機能や動作を表現する、関連したデータとコードを持つことができます。オブジェクトのデータ(しばしば関数も含む)はオブジェクトのパッケージの中(名前空間と呼ばれることがある)に適切に格納されます(カプセル化)。オブジェクトは一般に、ネットワークを通じて容易に送信することが可能な、データストアとしても使われます。

オブジェクトのテンプレートを定義する

学校の生徒と教師の情報を表示する、シンプルなプログラムを考えてみましょう。特定のプログラミング言語の文脈ではなく、 OOP 一般の理論を眺めていきます。

はじめに、最初のオブジェクトの記事にある、人物の包括的なデータや機能を定義した、 Person オブジェクトに戻りましょう。ある人物について知り得る事柄は数多くあります(住所、身長、靴のサイズ、 DNA 情報、パスポート番号、顕著な人格特性など)が、このケースでは名前、年齢、性別、趣味を表示することに興味があるだけです。また、このデータに基づいた短い自己紹介や、挨拶をさせられるようにもしましょう。これは抽象化 — より複雑な事物を、プログラムの目的に沿って簡単に操作できるように、その最も重要な側面を表現する、シンプルなモデルを作ること — として知られています。

実際のオブジェクトの生成

このクラスから、オブジェクトインスタンスを生成することができます。オブジェクトインスタンスは、クラスで定義されたデータや機能を持ったオブジェクトです。 Person クラスから、何名かの実際の人物を生成します。

クラスからオブジェクトインスタンスが生成されるとき、クラスのコンストラクター関数が生成のために実行されます。クラスからオブジェクトインスタンスが生成される過程をインスタンス化と呼びます。オブジェクトインスタンスは、クラスをインスタンス化したものです。

専門のクラス

このケースで求めているのは、包括的な人物ではなく、より特定のタイプである、教師と生徒です。OOP では、他のクラスを元にした新しいクラスを作ることができます。これらの新しい子クラスは、親クラスからデータやコード機能を継承することができ、すべてのオブジェクトタイプに共通する機能を、重複させるのではなく、再利用することができます。クラス間で機能が異なる場合は、必要に応じて特殊化された機能を直接定義することができます。

これは実に役立ちます。教師と生徒は名前、性別、年齢のように多数の共通機能を共有しており、これらの機能を一度だけ定義すればいいので便利です。異なるクラスで、同じ機能を分けて定義することもでき、その機能の各定義は異なる名前空間に置かれます。例えば、生徒の挨拶は "Yo, I'm [firstName]"(例:Yo, I'm Sam)という形式とし、一方の教師の挨拶は、より形式的に "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (例:Hello, My name is Mr Griffiths, and I teach Chemistry)のように。

: このような、同じ機能を複数のオブジェクトタイプが実装することを示す用語に、ポリモーフィズムがあります。不思議に感じている場合に備えて。

子クラスのオブジェクトインスタンスを生成しましょう。例:

記事の続きでは、OOP 理論が JavaScript でどのように実践されているかを見ていきます。

コンストラクターとオブジェクトインスタンス

JavaScript では、オブジェクトやその機能を定義するためにコンストラクター関数と呼ばれる特殊な関数を使用します。これは、よくオブジェクトがいくつ作成されるかが分からない状況になるので便利です。コンストラクターは必要な数のオブジェクトを効率的な方法で作成し、必要に応じてデータや関数を付加する手段を提供します。

新しいオブジェクトのインスタンスがコンストラクター関数によって生成されたとき、そのコアとなる機能(プロトタイプとして定義されており、 Object prototypes の記事で紹介しています)はプロトタイプチェーンと呼ばれる、参照の連鎖を通じてリンクされます。

JavaScript でコンストラクターを通じてクラスを作り、そこからオブジェクトのインスタンスを生成するところを見ていきましょう。まずは、最初のオブジェクトの記事で見た oojs.html ファイルの新しいコピーを、ローカルに作成したおいてください。

シンプルな例

  1. どのように通常の関数で人物を定義できるかを見てみるところから始めましょう。この関数を script 要素の中に加えてください。
    function createNewPerson(name) {
      var obj = {};
      obj.name = name;
      obj.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
      return obj;
    }
  2. この関数を呼び出すことで、新しい人物を生成することができます。次の 3 行をブラウザの JavaScript コンソールで試してみてください。
    var salva = createNewPerson('Salva');
    salva.name;
    salva.greeting();
    これも十分上手くいっていますが、やや長ったらしいです。オブジェクトを生成したいと知っているなら、なぜ明示的に空のオブジェクトを生成し、返すことが必要なのでしょうか?幸いにも、JavaScript はコンストラクター関数という形で、便利なショートカットを提供してくれます。早速作ってみましょう!
  3. 前の関数を、以下のもので置き換えてください。
    function Person(name) {
      this.name = name;
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
    }

コンストラクター関数は、JavaScript 版のクラスです。関数の中にすべての期待される機能を持っているが、何も返さない、あるいは明示的にオブジェクトを生成しないということが気になったかもしれません。基本的には、プロパティとメソッドを定義するだけです。加えて、this キーワードが使われていることに気づいたかもしれません。基本、オブジェクトインスタンスの 1 つが作成されるときにはいつでも、オブジェクトの name プロパティはコンストラクター呼び出しに渡される name 値と等しくなり、greeting() メソッドはコンストラクター呼び出しに渡される name 値も使用します。

メモ: 通常、コンストラクター関数の名前は大文字で始まります。コードの中で、コンストラクター関数がより容易に認識されるようにするための慣習です。

では、同じオブジェクトを生成するために、どのようにコンストラクターを呼び出すでしょうか?

  1. 次の 2 行を、前のコードの続きに加えてください。
    var person1 = new Person('Bob');
    var person2 = new Person('Sarah');
  2. コードを保存し、ブラウザをリロードして、以下の 4 行を JavaScript コンソールに入れて試してみてください。
    person1.name
    person1.greeting()
    person2.name
    person2.greeting()

素晴らしい!2 つの新しいオブジェクトが、異なる名前空間の下でページに格納されていることが確認できます。それらのプロパティとメソッドにアクセスするときには、person1 または person2 から始めなければなりません。他の機能と衝突してしまわないよう、これらの機能は適切にパッケージ化されています。しかしながら、それらは同じ name プロパティと greeting() メソッドが利用可能です。2 つのオブジェクトはそれぞれ、生成されたときに割り当てられた、自身の name 値を使っていることに注意してください。これが this を使うことがとても重要である理由の 1 つであり、他の値ではなく、自身の値を使っているのです。

コンストラクターをもう一度呼び出してみましょう。

var person1 = new Person('Bob');
var person2 = new Person('Sarah');

いずれのケースでも、新しいオブジェクトインスタンスを生成したいとブラウザに伝えるために new キーワードが使われており、その後に、括弧に必要なパラメータを入れた関数名が続き、その結果が変数に格納されていて、一般的な関数の呼ばれ方とよく似ています。どちらのインスタンスも、次の定義によって生成されています。

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

新しいオブジェクトが生成された後、person1 及び person2 変数は、次のオブジェクトを格納しています。

{
  name: 'Bob',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

{
  name: 'Sarah',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

コンストラクター関数を呼び出すとき、毎回 greeting() メソッドを定義していることに注意してください。これは理想的ではありません。これを回避するために、代わりにプロトタイプに関数を定義することができます。後で見てみましょう。

最終的なコンストラクターの作成

上で見てきた例は、スタートのためのシンプルな例に過ぎません。次は最終的な Person() コンストラクター関数を作りましょう。

  1. ここまでに挿入したコードを削除し、代わりとなるコンストラクターを追加してください。これはシンプルな例とほぼ同じもので、ほんのわずか複雑になっているだけです。
    function Person(first, last, age, gender, interests) {
      this.name = {
        first,
        last
      };
      this.age = age;
      this.gender = gender;
      this.interests = interests;
      this.bio = function() {
        alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
      };
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name.first + '.');
      };
    }
  2. ではその下に、コンストラクターからオブジェクトインスタンスを生成するため、次の行を追加してください。
    var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

ちょうど以前行ったように、プロパティやメソッドにアクセスできることを確認できるはずです。 JavaScript コンソールの中でやってみましょう。

person1['age']
person1.interests[1]
person1.bio()
// etc.

メモ: もしこの工程で何らかのトラブルがあった場合は、あなたのコードを我々のバージョン(oojs-class-finished.htmlライブサンプルも)と比べてみてください。

さらなる練習

まずはじめに、さらにいくつかのオブジェクトを生成する独自の行を追加し、オブジェクトインスタンスのメンバの取得や設定をしてみてください。

加えて、bio() メソッドにはいくつかの問題点があります。人物が女性である、あるいは他の優先される性別分類の場合でも、その出力には常に "He" という代名詞が含まれています。また、bio は interests 配列により多くのものが列挙されていても、2 つの趣味しか含めません。このクラス定義(コンストラクター)の問題を、あなたはどのように修正することができますか?コンストラクター内に任意のコード(恐らく、いくつかの条件分岐やループが必要となるでしょう)を入れてみてください。性別や、趣味の数が 1、2、あるいは 2 よりも多いかどうかによって、文がどのように構築されるべきか考えてみてください。

: もし行き詰まってしまった場合は、GitHubに答えとなるリポジトリライブ)があります。最初はあなた自身で書いてみてください!

オブジェクトインスタンスを生成する他の方法

ここまで、オブジェクトインスタンスを生成する 2 つの異なる方法を見てきました。オブジェクトリテラルの宣言と、上で見たコンストラクター関数の使用です。

これらは有意義ですが、他にも方法はあります。Web を巡る際に遭遇したときに備えて、よく知っておいてください。

Object() コンストラクター

まず最初に、Object() コンストラクターを新しいオブジェクトの生成のために使うことができます。はい、一般的なオブジェクトにも、空のオブジェクトを生成するコンストラクターがあります。

  1. このコードを JavaScript コンソールに入力してみましょう。
    var person1 = new Object();
  2. person1 変数に空のオブジェクトが格納されました。このオブジェクトに、ドット記法とブラケット記法を使ってプロパティを追加することができます。次の例を JavaScript コンソールで試してみましょう。
    person1.name = 'Chris';
    person1['age'] = 38;
    person1.greeting = function() {
      alert('Hi! I\'m ' + this.name + '.');
    };
  3. あらかじめプロパティやメソッドを設定するため、Object() コンストラクターに引数としてオブジェクトリテラルを渡すことも可能です。次のコードを JavaScript コンソールで試してみてください。
    var person1 = new Object({
      name: 'Chris',
      age: 38,
      greeting: function() {
        alert('Hi! I\'m ' + this.name + '.');
      }
    });

create() メソッドの使用

コードの順序についてもコンストラクターが助けとなります。コンストラクターを一箇所で作っておくと、必要に応じてインスタンスを生成することができ、それらがどこから来たものであるか、明瞭です。

しかしながら、特に少数のインスタンスのみを生成する場合に、最初にコンストラクターを作らずにインスタンスを生成することを好む人もいます。JavaScript にはそれを可能とする、create() と呼ばれる組み込みメソッドがあります。それにより、既存のオブジェクトを基にして、新しいオブジェクトを生成することができます。

  1. ブラウザーに読み込まれた以前のセクションからの練習を終えたら、 JavaScript コンソールで試してみてください。
    var person2 = Object.create(person1);
  2. 次は以下のコードです。
    person2.name
    person2.greeting()

erson1 を基に person2 が生成され、person2 では同じプロパティとメソッドが利用可能であることを確認することができます。

create() には、IE8 が対応していないという制限があります。つまり、コンストラクターはより古いブラウザに対応したい場合により効果的かもしれません。

いずれ、create() の効果についてより詳細に紹介するつもりです。

おさらい

この記事はオブジェクト指向の理論の概略を見てきました。これですべてではありませんが、ここで扱っていることに関する考えを提示しました。加えて、オブジェクトのインスタンスを生成する様々な方法を見始めたところです。

次の記事では、JavaScript オブジェクトのプロトタイプについて紹介します。

 

このモジュール内の文書

 

ドキュメントのタグと貢献者

 このページの貢献者: mfuji09, sii
 最終更新者: mfuji09,