JavaScript | ES6+ 文法:クラス構文 – インスタンス生成

JavaScript JavaScript
スポンサーリンク

「インスタンス生成」とは何か(まずイメージを掴む)

クラスは「設計図」、インスタンスは「その設計図から作られた実物」です。
class User { ... } は「ユーザーとはこういう性質・機能を持つ」という定義で、
new User("Alice") が「実際の1人のユーザー」を作る操作です。

インスタンス生成とは、この new クラス名(...) で「クラスから具体物を作ること」です。

class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(`こんにちは、${this.name} です`);
  }
}

const u1 = new User("Alice"); // ← これがインスタンス生成
const u2 = new User("Bob");   // ← これもインスタンス生成

u1.greet(); // こんにちは、Alice です
u2.greet(); // こんにちは、Bob です
JavaScript

ここが重要です。
class だけでは何も動きません。「インスタンスを生成して初めて動き出す」とイメージしてください。

new でインスタンスができるときに裏で何が起きているか

new クラス名(…) の裏側のざっくり流れ

new User("Alice") と書いたとき、裏ではだいたいこんなことが起きています。

1つ目として「空のオブジェクト」が作られる。
2つ目として「そのオブジェクトのプロトタイプ」が User.prototype に設定される。
3つ目として「User クラスの constructor が呼ばれ、this がその新しいオブジェクトになる」。
4つ目として「constructor の中で this に設定されたプロパティを持ったオブジェクト」が返される。

コードとして見えないだけで、new はここまで全部やってくれています。

class Counter {
  constructor(initial = 0) {
    this.value = initial;
  }

  increment() {
    this.value++;
  }
}

const c = new Counter(5); // ここで上の流れが全部起きている
c.increment();
console.log(c.value); // 6
JavaScript

ここが重要です。
インスタンス生成の本質は「constructor を通して this に初期データを詰め込んだオブジェクトを作る」こと。
new は、その一連の流れを一行で表現する魔法のキーワードです。

constructor とインスタンス生成の関係

constructor はインスタンス生成の「入口」です。
new クラス名(...) をした瞬間に、必ず一度だけ呼ばれます。

class Point {
  constructor(x, y) {
    console.log("constructor が呼ばれました");
    this.x = x;
    this.y = y;
  }
}

const p = new Point(3, 4);
// ログ: constructor が呼ばれました
JavaScript

constructor が無いクラスを定義しても、内部的には「何もしない constructor」がいると思って問題ありません。

class Empty { }

const e = new Empty(); // 暗黙の constructor() {} が呼ばれるイメージ
JavaScript

インスタンスごとの「違い」はどう作られるか

同じクラスから別々の状態を持つインスタンスが生まれる

クラスは同じでも、インスタンスは別物です。

class User {
  constructor(name) {
    this.name = name;
    this.loginCount = 0;
  }

  login() {
    this.loginCount++;
    console.log(`${this.name} がログイン (${this.loginCount} 回目)`);
  }
}

const u1 = new User("Alice");
const u2 = new User("Bob");

u1.login(); // Alice がログイン (1 回目)
u1.login(); // Alice がログイン (2 回目)
u2.login(); // Bob がログイン (1 回目)
JavaScript

u1u2 は、同じ User クラスから作られていますが、
this.namethis.loginCount の中身はそれぞれ違います。

ここが重要です。
「同じ設計図から、それぞれ別々の状態を持つ実体を複数作る」=クラスとインスタンスの関係
インスタンス生成は、この「別々の状態」を持つオブジェクトを量産するためにある、と捉えると理解しやすいです。

インスタンス間で共有されるのは「メソッド」

constructor の中で定義したもの(this.xxx)はインスタンスごとに違うデータですが、
クラス内に書いたメソッドは、すべてのインスタンスで共有されています。

class Example {
  constructor() {
    this.value = 0;
  }

  show() {
    console.log(this.value);
  }
}

const e1 = new Example();
const e2 = new Example();

console.log(e1.show === e2.show); // true
JavaScript

e1.showe2.show も、同じ関数(同じ場所を指している)です。
「データはインスタンスごとに違う」「メソッド(振る舞い)はクラスで共有」という役割分担になっています。

new を忘れたときのエラーと意味

new を付けずに呼ぶとどうなるか

class は必ず new を付けて使う必要があります。

class User {
  constructor(name) {
    this.name = name;
  }
}

const u = User("Alice"); // これはエラー
JavaScript

TypeError: Class constructor User cannot be invoked without 'new'
のようなエラーになります。

ここが重要です。
class は「普通の関数のように呼んではいけない」仕様になっています。
インスタンス生成は必ず new クラス名(...) という形で行う、と体で覚えてください。

引数付きのインスタンス生成とデフォルト値

「何を渡せばインスタンスを作れるのか」がそのクラスのインターフェース

constructor の引数は、そのクラスを使う側から見た「必須情報」です。

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

const r = new Rectangle(10, 5);
console.log(r.area()); // 50
JavaScript

「このクラスでインスタンスを作るには width と height を渡す必要がある」ということが、
constructor の引数から読み取れます。

デフォルト値でインスタンス生成を柔らかくする

ES6 のデフォルト引数を使えば、「よくある値」は省略可能にできます。

class Rectangle {
  constructor(width = 1, height = 1) {
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

const r1 = new Rectangle();      // 1x1
const r2 = new Rectangle(10);    // 10x1
const r3 = new Rectangle(10, 5); // 10x5

console.log(r1.area()); // 1
console.log(r2.area()); // 10
console.log(r3.area()); // 50
JavaScript

ここが重要です。
インスタンス生成の使いやすさは、constructor の引数設計でほぼ決まります。
「何を必須にして、何をデフォルトにするか」を考えるのは、クラス設計の大事な仕事です。

継承クラスのインスタンス生成と super

親クラスを継承したクラスでのインスタンス生成

extends を使うと、あるクラスを元にした「子クラス」を作り、そこからもインスタンスを生成できます。

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} が鳴いた`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);      // 親クラスの constructor を呼ぶ
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name} (${this.breed})「ワン!」`);
  }
}

const d = new Dog("ポチ", "柴犬");
d.speak(); // ポチ (柴犬)「ワン!」
JavaScript

new Dog("ポチ", "柴犬") のときは、
内部で super(name) を通じて Animal の constructor も呼ばれています。

ここが重要です。
子クラスのインスタンス生成では、「親の初期化 → 子の初期化」という2段階が必ずある。
だから子クラスの constructor では最初に super(...) を呼ぶ必要がある、というわけです。

インスタンス生成と static メソッドの関係

クラスから「便利な生成メソッド」を用意する

インスタンス生成のロジックを static メソッドに切り出すこともよくあります。

class User {
  constructor(name, admin = false) {
    this.name = name;
    this.admin = admin;
  }

  static createAdmin(name) {
    return new User(name, true);
  }

  describe() {
    console.log(`${this.name} (${this.admin ? "管理者" : "一般"})`);
  }
}

const u1 = new User("Alice");
const u2 = User.createAdmin("Bob"); // ここでも new User が使われている

u1.describe(); // Alice (一般)
u2.describe(); // Bob (管理者)
JavaScript

ここでやっていることは、

  • 基本のインスタンス生成方法:new User(name, admin)
  • よく使うパターンを名前付きで用意:User.createAdmin(name)

という「インスタンス生成のレシピ」をクラス側に持たせる設計です。

例題でインスタンス生成の感覚を固める

例1:ゲームキャラをクラス+インスタンスで表す

class Player {
  constructor(name, hp = 100) {
    this.name = name;
    this.hp = hp;
  }

  damage(amount) {
    this.hp = Math.max(0, this.hp - amount);
  }

  show() {
    console.log(`${this.name} HP: ${this.hp}`);
  }
}

const p1 = new Player("勇者");
const p2 = new Player("魔法使い", 80);

p1.damage(30);
p2.damage(50);

p1.show(); // 勇者 HP: 70
p2.show(); // 魔法使い HP: 30
JavaScript

同じ Player クラスから作った2人のキャラですが、HP も名前も別々です。

例2:設定オブジェクトをクラスで包む

class Config {
  constructor(options = {}) {
    this.theme = options.theme ?? "light";
    this.lang = options.lang ?? "ja";
    this.debug = options.debug ?? false;
  }

  describe() {
    console.log(`theme=${this.theme}, lang=${this.lang}, debug=${this.debug}`);
  }
}

const cfg1 = new Config();
const cfg2 = new Config({ theme: "dark", debug: true });

cfg1.describe(); // theme=light, lang=ja, debug=false
cfg2.describe(); // theme=dark, lang=ja, debug=true
JavaScript

インスタンス生成時にオプションを渡して、
内部でマージ・初期化しているパターンです。

まとめ

インスタンス生成の核心は、
new クラス名(...) で、設計図(class)から具体的なオブジェクト(インスタンス)を作る」 ことです。

押さえておきたいポイントは次の通りです。

  • new を付けてクラスを呼ぶと、そのたびに新しいインスタンスができる
  • 生成時には必ず constructor が一度呼ばれ、this に初期データがセットされる
  • 同じクラスでも、インスタンスごとにプロパティ(状態)は別々
  • メソッドはクラスで共有され、インスタンスから呼び出して使う
  • 継承クラスでは、インスタンス生成時に super(...) を通じて親の初期化も行われる
  • static メソッドを使うと、よく使う生成パターンをまとめて表現できる

まずは小さなクラスを作って、new でインスタンスを何個か生成し、
「このインスタンスとこのインスタンスはどこが同じでどこが違うか」を意識して眺めてみてください。
そこに、クラスとインスタンス生成の“おいしいところ”が全部詰まっています。

タイトルとURLをコピーしました