コンストラクタ (ES5 以前)

ここでは ES5 以前の JavaScript における、オブジェクト指向プログラミングについて説明します。 内容は ES6 以降も有効であり、依然として多くのコードで使われているので、知っておいた方が良いことではあります。 しかし、新しいコードには class キーワードによるクラス定義の方が良いかもしれません。

classキーワードを用いたクラス定義については、「JavaScript のクラス定義」をみてください。

コンストラクタはオブジェクトを作成して初期化する

コンストラクタは、オブジェクトを作成し、初期化するための関数オブジェクトです。

例えば、Person クラスのコンストラクタが、名前 name と 年齢age の二つの値を受け取るなら、次のようにコンストラクタを定義できます。

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

普通の関数の定義と同じです。引数としてnameageを受け取っています。

名前は必ずしも大文字にする必要はありません。しかし、コンストラクタの名前 (つまりクラス名) は一般的な慣例として、パスカルケース (大文字キャメルケース) で名付けます。このため Person の最初の文字は大文字にしています。

特徴的なのは、コンストラクタの中でthisキーワードにプロパティを作って値を保存している点です。

こうして定義したPersonコンストラクタを使ってPersonオブジェクトを作るには、 newキーワードを付けてコンストラクタを呼び出します

let p = new Person('Ichiro', 30);

console.log(`Name: ${p.name}`);
console.log(` Age: ${p.age}`);

この実行結果は次の通りです。

確かに p.namep.age に、コンストラクタで渡した値がセットされています。

コンストラクタの中で this で参照していたのは、新しく作成されたオブジェクトそのものだからです。

試しに、コンストラクタの中でthisを出力してみましょう。

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

let p = new Person('Ichiro', 30);

確かに次のようにthisPersonオブジェクトであることがわかりますね。

new を付けないで呼び出した時

コンストラクタの定義の仕方は、基本的に通常の関数と同じです。

このため、いくら開発者の気持ちとして、それをコンストラクタにするつもりだったとしても、 newを付けないで呼び出せば、当然ながらオブジェクトは作成されません。

試しに上のコードを、new 無しで実行してみましょう。

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

let p = Person('Ichiro', 30); // new を付けていない

これを実行すると、次のような内容が表示されます。

newを付けないで Person関数を呼び出したので、 thisGlobal オブジェクトであるWindow オブジェクトをポイントしています。

Global オブジェクトは環境によって異なります。ブラウザ上では Window オブジェクトですが、Node.js 上で試している場合は Global オブジェクトはglobal というオブジェクトです。

従って、this.namethis.ageは Global オブジェクトのプロパティとして値がセットされたことになります。エラーではありませんが、本来意図したことではありませんね。

new 無しの場合に備える new.target

newを付けて関数を呼び出した場合 (つまり、コンストラクタとして呼び出した場合)、 new.targetはそのコンストラクタ自身を指します。 一方、newを付けないで呼び出した時には、 new.targetundefinedになります。

これを利用することで、誤ってnew無しで呼び出された場合にも、 オブジェクトが生成されるようにもできます。

function Person(name, age) {
  if (!new.target) {
      return new Person(name, age);
  }
  this.name = name;
  this.age = age;
}

ただし、このクラスから継承するクラスがある場合には、たいてい派生クラスのコンストラクタからこのコンストラクタを呼ぶ必要があります。 このときにはnew無しで呼ぶことになります。このため、new無しで呼ぶことを禁止する場合には、クラスの継承関係にも注意が必要です。

コンストラクタはプロトタイプオブジェクトをコピーする

コンストラクタ (関数オブジェクト) はそれぞれ、関連付けされたプロトタイプオブジェクトを持っています。

新しいオブジェクトの prototype プロパティには、コンストラクタの prototype オブジェクトへの参照がセットされます。 これによって、オブジェクト間の継承関係を定義したり、共有プロパティを実装するために利用することができます。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 JavaScript 入門