constructor とは何か(まずはイメージから)
constructor は、class から new したときに「最初に一回だけ」呼ばれる特別なメソッドです。
役割はシンプルで、
- インスタンスが生まれた瞬間に
- そのインスタンスが持つべき初期データ(プロパティ)をセットする
ための場所です。
class User {
constructor(name) {
this.name = name; // インスタンスごとのデータ
}
greet() {
console.log(`こんにちは、${this.name} です`);
}
}
const u = new User("Alice"); // ここで constructor が呼ばれる
u.greet(); // こんにちは、Alice です
JavaScript「new User("Alice") と書いたときに、constructor(name) が呼ばれ、
その中で this.name = name としている」というイメージを持ってください。
ここが重要です:constructor の中で this に何を持たせるかが、そのクラスの“型”を決めると思っていいです。
constructor と this の関係(ここはしっかり理解してほしい)
this は「今まさに生成中のインスタンス」
constructor の中で出てくる this は、そのとき new されているインスタンスそのものを指します。
class Counter {
constructor(initialValue = 0) {
this.value = initialValue;
}
increment() {
this.value++;
}
show() {
console.log(this.value);
}
}
const c1 = new Counter(10);
const c2 = new Counter(100);
c1.increment();
c1.show(); // 11
c2.increment();
c2.show(); // 101
JavaScriptc1 を作るときの constructor の中では this === c1、c2 を作るときは this === c2 です。
この「インスタンスごとに別々の this」があるからこそ、
同じクラスから作ったインスタンスでも、違うデータを持てます。
ここが重要です:
constructor の中で this.xxx = ... と書くたびに、「インスタンスの属性(プロパティ)」が決まっていく。
「このクラスのインスタンスは何を持つのか?」を、constructor で設計する感覚です。
constructor がなくてもクラスは動く(暗黙のデフォルト)
class の中に constructor を書かなかった場合、
「何もしない空の constructor」が自動的に用意されます。
class Empty {
}
const e = new Empty(); // エラーにならない(暗黙の constructor がある)
JavaScriptつまり、
class Empty {
}
JavaScriptは、実質こう書いたのと同じです。
class Empty {
constructor() {}
}
JavaScriptただし、自分で初期化したいプロパティが出てきたら、
自分で constructor を定義する必要があります。
引数とデフォルト値(使い勝手を決める大事な部分)
引数で「生成に必要な情報」を受け取る
constructor の引数は、そのクラスのインスタンスを作るために「外から渡してほしい情報」です。
class User {
constructor(id, name, role) {
this.id = id;
this.name = name;
this.role = role;
}
describe() {
console.log(`${this.id}: ${this.name} (${this.role})`);
}
}
const u = new User(1, "Alice", "admin");
u.describe(); // 1: Alice (admin)
JavaScript何を引数にするかを考えるのは、「このクラスをどう使わせたいか」という設計の中心になります。
デフォルト引数で「使い方を優しくする」
ES6 の関数と同じように、constructor でもデフォルト引数が使えます。
class User {
constructor(name, role = "user") {
this.name = name;
this.role = role;
}
describe() {
console.log(`${this.name} (${this.role})`);
}
}
const u1 = new User("Alice");
const u2 = new User("Bob", "admin");
u1.describe(); // Alice (user)
u2.describe(); // Bob (admin)
JavaScriptここが重要です:
「よくあるケース」は引数を省略できるようにしておくと、クラスが使いやすくなる。
constructor の引数とデフォルト値は、「このクラスの使い勝手」を決めるインターフェースだと考えると良いです。
「constructor は1つだけ」「オーバーロードはない」
同じ class に constructor を2つは書けない
他の言語(Java, C# など)には「コンストラクタのオーバーロード」という概念がありますが、
JavaScript の class は constructor を1つしか持てません。
class User {
// これは NG
// constructor(name) { ... }
// constructor(id, name) { ... }
}
JavaScriptこういうことはできないので、「引数のパターン」を自前で解釈する必要があります。
パラメータの解釈で柔軟にする
例えば、「文字列1つなら name として扱い、オブジェクトなら id と name を取る」
というような実装も書けます。
class User {
constructor(arg) {
if (typeof arg === "string") {
this.id = null;
this.name = arg;
} else if (typeof arg === "object") {
this.id = arg.id ?? null;
this.name = arg.name ?? "no-name";
}
}
describe() {
console.log(`${this.id}: ${this.name}`);
}
}
const u1 = new User("Alice");
const u2 = new User({ id: 1, name: "Bob" });
u1.describe(); // null: Alice
u2.describe(); // 1: Bob
JavaScriptここが重要です:
constructor は1つしか書けないので、「引数の形を見て中で振り分ける」という設計をする。
ただし、複雑にしすぎると読みづらくなるので、
初心者のうちは「素直な引数にしておく」ほうが分かりやすいことが多いです。
継承時の constructor と super(ここはつまずきやすい)
子クラスに constructor を書くときは super が必須
extends を使って継承する場合、
子クラスで constructor を定義するなら、必ず最初に super(...) を呼ばなければなりません。
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // 親の constructor を呼ぶ(必須)
this.breed = breed;
}
info() {
console.log(`${this.name} (${this.breed})`);
}
}
const d = new Dog("ポチ", "柴犬");
d.info(); // ポチ (柴犬)
JavaScriptもし super(name) を書かずに this.breed = ... といった操作をしようとすると、エラーになります。
class BadDog extends Animal {
constructor(name, breed) {
// super(name); を忘れると…
this.breed = breed; // ここでエラー
}
}
JavaScriptここが重要です:
子クラスの constructor では、「親の初期化」→「子の初期化」の順番が必須。
constructor を書いたら、「まず super(...)」と体で覚えるくらいでちょうどいいです。
親の constructor をそのまま使うなら、子は書かなくてよい
子クラス独自の初期化が不要で、
親の constructor に引数をそのまま渡せばいいだけなら、子クラスに constructor を書かなくてもOKです。
class Animal {
constructor(name) {
this.name = name;
}
}
class Dog extends Animal {
// constructor を書かない場合、
// constructor(...args) { super(...args); } が自動生成されるイメージ
}
const d = new Dog("ポチ");
console.log(d.name); // ポチ
JavaScriptconstructor は「必要になったら追加する」ものであって、
無理に子クラスに書かなくてもよい、ということも覚えておいてください。
constructor とクラスフィールド/メソッドの役割分担
constructor では「初期データ」だけに集中する
class の中には、いろいろ書けますが、役割を分けて考えると整理しやすくなります。
- constructor:インスタンスの初期状態を決める(this にプロパティをセット)
- インスタンスメソッド:インスタンスに対する操作
- static メソッド:クラスに対するユーティリティ的な操作
例えばユーザークラスなら、こんな感じです。
class User {
constructor(id, name) {
this.id = id;
this.name = name;
this.active = true;
}
deactivate() {
this.active = false;
}
describe() {
console.log(`${this.id}: ${this.name} (${this.active ? "有効" : "無効"})`);
}
static createGuest() {
return new User(0, "Guest");
}
}
JavaScriptここが重要です:
constructor の中で「ロジック(計算)」をやりすぎない。
できるだけ、「初期値を決める場所」としてシンプルに保ったほうが、クラス全体の見通しが良くなります。
よくある勘違いと注意点(重要な落とし穴)
constructor を普通のメソッドのように呼べない
constructor は特別なメソッドで、自分で直接呼ぶことはできません。
class User {
constructor(name) {
this.name = name;
}
}
const u = new User("Alice"); // OK(constructor が裏で呼ばれる)
// u.constructor("Bob"); // こういう使い方はしない
JavaScript「作成済みのインスタンスを再初期化したい」という場合は、
constructor を呼び直すのではなく、別のメソッド(reset や initFrom など)を自分で用意するべきです。
this を使う前に super を呼ぶ必要がある(子クラス)
さきほど触れましたが、子クラスの constructor では super() より前に this を触るとエラーになります。
これは JavaScript のクラス仕様上のルールです。
class Parent {
constructor() {
this.value = 1;
}
}
class Child extends Parent {
constructor() {
// this.value = 2; // ここで触るとエラー
super();
this.value = 2; // これは OK
}
}
JavaScript「extends しているクラスに constructor を書いたら、まず super」と、
条件反射で書けるようにしてしまうのが一番楽です。
例題で理解を固める
例1:シンプルなクラスと constructor
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
distanceFromOrigin() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
}
const p = new Point(3, 4);
console.log(p.distanceFromOrigin()); // 5
JavaScript例2:デフォルト値とオプション的な引数
class Button {
constructor(label, color = "blue") {
this.label = label;
this.color = color;
}
describe() {
console.log(`ボタン [${this.label}] 色: ${this.color}`);
}
}
const b1 = new Button("OK");
const b2 = new Button("Cancel", "red");
b1.describe(); // ボタン [OK] 色: blue
b2.describe(); // ボタン [Cancel] 色: red
JavaScript例3:継承と super
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`こんにちは、${this.name} です`);
}
}
class Employee extends Person {
constructor(name, department) {
super(name); // Person の constructor を呼ぶ
this.department = department;
}
greet() {
super.greet(); // 親の greet も使いつつ…
console.log(`部署: ${this.department}`);
}
}
const e = new Employee("Alice", "開発");
e.greet();
// こんにちは、Alice です
// 部署: 開発
JavaScript例4:インスタンス生成用の static メソッドと組み合わせる
class Config {
constructor(options) {
this.theme = options.theme ?? "light";
this.lang = options.lang ?? "ja";
}
static fromEnv(env) {
return new Config({
theme: env.THEME,
lang: env.LANG
});
}
}
const env = { THEME: "dark", LANG: "en" };
const cfg = Config.fromEnv(env);
console.log(cfg.theme, cfg.lang); // dark en
JavaScriptまとめ
constructor の本質は、
「class からインスタンスを作るとき、そのインスタンスの最初の状態を決める場所」 です。
押さえておきたいポイントを整理すると、
constructor(...) { ... }はnewされた瞬間に一度だけ呼ばれる- 中の
thisは「そのとき生成中のインスタンス」を指す this.xxx = ...で「インスタンスが持つプロパティ(型)」を設計する- constructor は1つだけ。柔軟さが必要なら引数の解釈で工夫する
- 継承時に子クラスで constructor を書くなら、必ず最初に
super(...)を呼ぶ - constructor では「初期化」に集中させ、複雑な処理は通常メソッドや static に分離する
この感覚がつかめると、class 全体の理解も一気に楽になります。
小さなクラスをいくつか自分で作って、「どのプロパティを constructor で初期化するか」を意識しながら書いてみると、手に馴染んでいきます。
