クラスとは
クラスはオブジェクトの設計図です。ES6で導入された構文糖衣で、内部的にはプロトタイプベースの継承を使っています。
クラスの定義
class User {
// コンストラクタ(初期化処理)
constructor(name, age) {
this.name = name;
this.age = age;
}
// メソッド
greet() {
return `こんにちは、${this.name}です`;
}
// ゲッター
get profile() {
return `${this.name} (${this.age}歳)`;
}
// セッター
set profile(value) {
const [name, age] = value.split(",");
this.name = name;
this.age = parseInt(age);
}
}
// インスタンス化
const user = new User("太郎", 25);
console.log(user.name); // "太郎"
console.log(user.greet()); // "こんにちは、太郎です"
console.log(user.profile); // "太郎 (25歳)"
user.profile = "花子,30";
console.log(user.name); // "花子"
プロパティ
パブリックプロパティ
class Product {
// クラスフィールド(ES2022)
name = "";
price = 0;
inStock = true;
constructor(name, price) {
this.name = name;
this.price = price;
}
}
const product = new Product("本", 1500);
console.log(product.inStock); // true
プライベートプロパティ
#プレフィックスでプライベートにします(ES2022)。
class BankAccount {
#balance = 0; // プライベート
constructor(initialBalance) {
this.#balance = initialBalance;
}
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
return amount;
}
return 0;
}
get balance() {
return this.#balance;
}
}
const account = new BankAccount(1000);
account.deposit(500);
console.log(account.balance); // 1500
// console.log(account.#balance); // エラー:外部からアクセス不可
静的プロパティとメソッド
インスタンスではなくクラス自体に属します。
class MathUtils {
static PI = 3.14159;
static add(a, b) {
return a + b;
}
static multiply(a, b) {
return a * b;
}
}
// クラス名で直接アクセス
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(1, 2)); // 3
// インスタンスからはアクセスできない
// const math = new MathUtils();
// math.add(1, 2); // エラー
静的プライベート
class Counter {
static #count = 0; // 静的プライベート
constructor() {
Counter.#count++;
}
static getCount() {
return Counter.#count;
}
}
new Counter();
new Counter();
console.log(Counter.getCount()); // 2
クラス式
クラスは式としても定義できます。
// 無名クラス式
const User = class {
constructor(name) {
this.name = name;
}
};
// 名前付きクラス式
const Product = class ProductClass {
constructor(name) {
this.name = name;
}
};
const user = new User("太郎");
const product = new Product("本");
this の扱い
メソッド内の this
class User {
constructor(name) {
this.name = name;
}
greet() {
console.log(`こんにちは、${this.name}です`);
}
}
const user = new User("太郎");
user.greet(); // "こんにちは、太郎です"
// thisが失われるケース
const greetFn = user.greet;
// greetFn(); // エラー:this is undefined
アロー関数で this を保持
class Timer {
constructor() {
this.seconds = 0;
}
start() {
// アロー関数は外側のthisを保持
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
}
const timer = new Timer();
timer.start();
bind でthisを固定
class Button {
constructor(label) {
this.label = label;
// メソッドにthisをバインド
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(`${this.label}がクリックされました`);
}
}
const button = new Button("送信");
document.addEventListener("click", button.handleClick);
クラスフィールドでアロー関数
class Button {
label = "ボタン";
// アロー関数でthisを自動バインド
handleClick = () => {
console.log(`${this.label}がクリックされました`);
};
}
instanceof と型チェック
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true
// コンストラクタ名
console.log(dog.constructor.name); // "Dog"
プロトタイプベースの理解
クラスは内部的にプロトタイプを使っています。
class User {
constructor(name) {
this.name = name;
}
greet() {
return `こんにちは、${this.name}です`;
}
}
// 上記は以下と同等
function UserOld(name) {
this.name = name;
}
UserOld.prototype.greet = function() {
return `こんにちは、${this.name}です`;
};
// プロトタイプチェーン
const user = new User("太郎");
console.log(Object.getPrototypeOf(user) === User.prototype); // true
実践例:商品カタログ
class Product {
static #nextId = 1;
#id;
name;
price;
#stock;
constructor(name, price, stock = 0) {
this.#id = Product.#nextId++;
this.name = name;
this.price = price;
this.#stock = stock;
}
get id() {
return this.#id;
}
get stock() {
return this.#stock;
}
get isAvailable() {
return this.#stock > 0;
}
addStock(quantity) {
if (quantity > 0) {
this.#stock += quantity;
}
}
sell(quantity = 1) {
if (quantity <= this.#stock) {
this.#stock -= quantity;
return true;
}
return false;
}
toString() {
return `[${this.#id}] ${this.name}: ¥${this.price} (在庫: ${this.#stock})`;
}
}
// 使用例
const book = new Product("JavaScript入門", 2800, 10);
console.log(book.toString()); // "[1] JavaScript入門: ¥2800 (在庫: 10)"
book.sell(3);
console.log(book.stock); // 7
const laptop = new Product("ノートPC", 120000, 5);
console.log(laptop.id); // 2
まとめ
classでオブジェクトの設計図を定義constructorで初期化処理#プレフィックスでプライベート化staticでクラスメンバーを定義get/setでアクセサを定義- アロー関数やbindでthisを管理
次回は継承とオブジェクト指向について学びます。