JavaScriptのclassを使いこなせるようになりたい!でも難しそう…。そんな悩みを持つ方も多いのではないでしょうか?
実はclassを使うと、コードがすっきりと整理され、メンテナンスも楽になります。この記事では、実践的な例を交えながら、classの基礎から活用方法までをやさしく解説します。
これを読めば、あなたもclassを使った効率的なコーディングができるようになります!
クラスとは何か?
JavaScriptのclassは、オブジェクト指向プログラミングの考え方を取り入れた機能です。似た性質を持つデータとその処理方法をひとまとめにできる便利な「設計図」のようなものです。
たとえば、ECサイトでユーザー情報を管理する場合を考えてみましょう:
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
// ユーザー情報を表示するメソッド
showProfile() {
return `名前: ${this.name}, メール: ${this.email}`;
}
}
// 使用例
const user = new User('山田太郎', 'yamada@example.com');
console.log(user.showProfile()); // "名前: 山田太郎, メール: yamada@example.com"
このように、classを使うことで関連するデータと機能を1つにまとめることができ、コードの見通しが良くなります。
次の章では、このclassの基本的な書き方について詳しく見ていきましょう。
クラスの基本構文
クラスの基本的な書き方を見ていきましょう。クラスを宣言するには class
キーワードを使います。
コンストラクタ
class Dog {
// コンストラクタ(初期化処理)
constructor(name, age) {
this.name = name; // プロパティの設定
this.age = age; // プロパティの設定
}
// メソッド(クラスの中で定義する関数)
bark() {
return `${this.name}が吠えました!`;
}
}
クラスの使用方法
// クラスからインスタンスを作成
const pochi = new Dog('ポチ', 3);
// メソッドの呼び出し
console.log(pochi.bark()); // "ポチが吠えました!"
console.log(pochi.name); // "ポチ"
console.log(pochi.age); // 3
ポイント:
constructor
は特別なメソッドで、インスタンス作成時に自動的に実行されますthis
はインスタンス自身を指します- プロパティはインスタンスごとに固有の値を持ちます
- メソッドはすべてのインスタンスで共有されます
クラスの継承
クラスの継承を使うと、既存のクラスの機能を引き継ぎながら、新しい機能を追加できます。
// 基底クラス(親クラス)
class Animal {
constructor(name) {
this.name = name;
}
makeSound() {
return "...";
}
}
// Animalクラスを継承した子クラス
class Cat extends Animal {
constructor(name, color) {
super(name); // 親クラスのコンストラクタを呼び出し
this.color = color;
}
makeSound() {
return "にゃー!"; // メソッドのオーバーライド
}
scratch() {
return `${this.color}の${this.name}が引っかきました`;
}
}
// 使用例
const tama = new Cat('タマ', '白');
console.log(tama.makeSound()); // "にゃー!"
console.log(tama.scratch()); // "白のタマが引っかきました"
重要ポイント:
extends
で継承を実装super()
で親クラスのコンストラクタを呼び出し- 子クラスで同名メソッドを定義すると上書き(オーバーライド)される
- 親クラスのメソッドと新しいメソッドの両方が使える
クラスの重要な機能
staticメソッド
インスタンス化せずに使えるクラスメソッドを定義できます。
class Calculator {
static add(a, b) {
return a + b;
}
}
console.log(Calculator.add(5, 3)); // 8
// インスタンス化不要で直接使用可能
getterとsetter
プロパティのアクセスと設定を制御できます。
class BankAccount {
#balance = 0; // プライベートフィールド
get balance() {
return `¥${this#balance}`;
}
set balance(value) {
if (value < 0) {
throw new Error('残高はマイナスにできません');
}
this.#balance = value;
}
}
const account = new BankAccount();
account.balance = 1000;
console.log(account.balance); // "¥1000"
プライベートフィールド
#
記号を使って、クラス外からアクセスできないプロパティを定義できます。
- 外部からの不正なアクセスを防ぐ
- カプセル化を実現
これらの機能を使うことで、より安全で保守性の高いコードが書けます。
実践的な使用例
実際のプロジェクトでよく使用される具体例を見てみましょう:
// ショッピングカートの実装例
class ShoppingCart {
#items = [];
addItem(item) {
this.#items.push(item);
}
removeItem(itemId) {
this.#items = this.#items.filter(item => item.id !== itemId);
}
get totalPrice() {
return this.#items.reduce((sum, item) => sum + item.price, 0);
}
checkout() {
if (this.#items.length === 0) {
throw new Error('カートが空です');
}
return {
items: this.#items,
total: this.totalPrice,
date: new Date()
};
}
}
// 使用例
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: 'Tシャツ', price: 2000 });
cart.addItem({ id: 2, name: 'ジーンズ', price: 5000 });
console.log(cart.totalPrice); // 7000
console.log(cart.checkout()); // { items: [...], total: 7000, date: ... }
このように、クラスを使うことで:
- データ(商品)と処理(追加・削除・会計)を1つにまとめられる
- プライベートフィールドで内部データを保護
- getterで合計金額の計算を自動化
- メソッドで複雑な処理をカプセル化
実務では、このような形でクラスを活用することが多いでしょう。
よくある落とし穴と対処法
1. thisの挙動の問題
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// 問題のあるコード
setInterval(function() {
this.seconds++; // thisがWindowを指してしまう
}, 1000);
// 正しい実装
setInterval(() => {
this.seconds++; // アロー関数でthisを保持
}, 1000);
}
}
2. 継承時の注意点
class Parent {
constructor() {
this.init(); // 初期化メソッド
}
}
class Child extends Parent {
// 問題: superの呼び出し忘れ
constructor() {
// super()を呼び出す必要がある
this.name = 'Child'; // エラー発生
}
}
// 正しい実装
class Child extends Parent {
constructor() {
super(); // 親のコンストラクタを先に呼び出す
this.name = 'Child'; // OK
}
}
これらの問題は初心者がよく遭遇するものです。特にthisの挙動には注意が必要で、アロー関数を使うことで多くの問題を解決できます。
まとめ
クラスの使用は、モダンなJavaScriptプログラミングの重要なスキルです。
クラス使用のベストプラクティス
- 単一責任の原則を守る(1つのクラスは1つの役割に集中)
- プライベートフィールドを活用してデータを保護
- getterとsetterで適切なデータアクセス制御を実装
- 継承は必要な場合のみ使用(過度な継承は避ける)
従来のプロトタイプベースとの比較
// 従来のプロトタイプベース
function User(name) {
this.name = name;
}
User.prototype.sayHi = function() {
return `Hi, ${this.name}!`;
};
// モダンなクラス構文
class User {
constructor(name) {
this.name = name;
}
sayHi() {
return `Hi, ${this.name}!`;
}
}
クラス構文を使うことで:
- コードが読みやすくなる
- オブジェクト指向的な設計が容易に
- 他の言語経験者にも理解しやすい
この記事で学んだ知識を活かして、ぜひ実践的なコード作成に挑戦してみてください!