こんにちは!フロントエンドの現場を長年うろついているチーフアーキテクトです。
JavaScriptのコードを書いているとき、「あれ?さっきは文字列だったはずの変数に、いつの間にか数値が混ざってバグってる……!」なんて冷や汗をかいた経験、ありませんか? 大丈夫です、それ、世界中のすべてのプログラマーが通る道ですから安心してください。
今日は、そんなJavaScriptの「型のゆらぎ」から私たちのコードを守り、さらにアプリの設計を劇的に美しくしてくれる「値オブジェクト(Value Object)」という秘密兵器について、お話ししようと思います。
難しい専門用語はなるべく抜きにして、身近な「お買い物」のイメージから優しく紐解いていきましょう!
—
そもそも「値オブジェクト」ってなに?
いきなりコードを書く前に、身の回りのもので考えてみましょう。
例えば、お財布に入っている「1,000円札」を思い浮かべてください。
ただの紙切れですが、そこには「1,000円であること」という絶対的な価値と意味が宿っていますよね。もし裏に落書きをしても、破れていなければ価値は1,000円のままです。
プログラミングの世界でも同じです。
私たちが何気なく使う「メールアドレス」や「年齢」「価格」といったデータは、ただの「文字(String)」や「数字(Number)」ではありません。
- メールアドレスなら、「アットマーク(@)が含まれているか?」というルールがある。
- 年齢なら、「マイナスになってはいけない」というルールがある。
こうした「データのルールや意味」を、ひとつの頑丈なカプセル(クラス)に閉じ込めてしまおう! というのが、値オブジェクトの考え方です。
—
なぜJavaScriptで値オブジェクトが必要なの?
JavaScriptは、とても自由で優しい言語です。変数になんでも入れられるし、型のチェックもゆるふわです。
でも、その「優しさ」が、実務の現場では時々牙を剥きます。
例えば、ECサイトで商品の価格を計算する機能を考えてみましょう。
// 普通によくある書き方
let price = 1000; // 1000円
// うっかり文字列が混ざっちゃった!
price = “1000円”;
// 割引計算をすると……あれ?
let discounted = price – 100; // “1000円” – 100 = ??? (NaNになっちゃう!)
JavaScriptのエンジンは、「”1000円”から100を引けと言われたから頑張ってみたけど、無理だった……」と、頭を抱えてしまいます(これが`NaN`という絶望の合図ですね)。
こういう「不正なデータがいつの間にか入り込んでアプリをクラッシュさせる魔物」を退治するために、値オブジェクトが役に立ちます。
—
実践!「金額」を表す値オブジェクトを作ってみよう
それでは実際に、JavaScriptのクラスを使って、お買い物で使える安全な「価格(Money)」の値オブジェクトを作ってみましょう。
エディタを開く準備はいいですか? 一緒に見ていきましょうね。
/
- 金額を表す値オブジェクト(Value Object)
/
class Price {
// 内部で保持する実際の数値
#value;
constructor(amount) {
// 1. ルールチェック:数値じゃなかったら怒る!
if (typeof amount !== “number”) {
throw new Error(“価格は数値で指定してくださいね。”);
}
// 2. ルールチェック:マイナスだったら怒る!
if (amount < 0) {
throw new Error("価格にマイナスは指定できません。");
}
// 3. ルールチェック:小数点以下が変だったら丸めるなどの処理を入れることも
this.#value = amount;
}
// 値を取り出すためのメソッド(ゲッター)
get value() {
return this.#value;
}
// 表示用のフォーマットを作るメソッド(ドメインロジックの同梱)
toDisplayString() {
return `¥${this.#value.toLocaleString()}`;
}
// 足し算をするメソッド(不変性を守るため、新しいオブジェクトを返すのがコツ!)
add(otherPrice) {
// 他のPriceクラスのインスタンスかどうかチェックするとさらに安全
if (!(otherPrice instanceof Price)) {
throw new Error("足し算できるのはPrice同士だけです。");
}
return new Price(this.#value + otherPrice.value);
}
}
// ==========================================
// 実際に使ってみましょう!
// ==========================================
try {
// 正しい使い方の例
const applePrice = new Price(120); // りんご代 120円
const orangePrice = new Price(150); // みかん代 150円
console.log(applePrice.toDisplayString()); // 出力: ¥120
// 合計を計算する
const totalPrice = applePrice.add(orangePrice);
console.log(totalPrice.toDisplayString()); // 出力: ¥270
// ⚠️ やってはいけないこと(不正な値)のテスト
// const weirdPrice = new Price(-50); // => エラー:「価格にマイナスは指定できません。」
// const stringPrice = new Price(“120円”); // => エラー:「価格は数値で指定してくださいね。」
} catch (error) {
console.error(“エラーが発生しました:”, error.message);
}
コードのポイント解説
1. カプセル化(`#value`)
クラスの頭に付いている `#` は、JavaScriptの最新機能である「プライベートフィールド」です。外から直接 `price.#value = 9999` のように書き換えられないよう、ガッチリ鍵をかけています。
2. 不変性(Immutability)
一度作った `Price` オブジェクトの中身を後から変更することはできません。もし金額を変えたくなったら、新しく `new Price(…)` を作り直します。これにより、「いつの間にか値が変わっていた」というバグが根絶やしにされます。
3. 振る舞い(メソッド)の同居
単なるデータだけでなく、「表示用の文字列にする方法(`toDisplayString`)」や「足し算のルール(`add`)」をデータの近くに持たせています。これにより、「あっちのファイルでもこっちのファイルでも、同じ金額のフォーマット書き方をしてバグる」という悲劇を防げます。
—
まとめ:あなたのコードを「優しく、強く」する設計へ
今回は、値オブジェクトの基本と、JavaScriptでの実装方法についてお伝えしました。
最初は「たかが数字をクラスで包むなんて、コード量が増えてめんどくさいな」と感じるかもしれません。大丈夫、私も最初はそう思っていました(笑)。
ですが、アプリの規模が大きくなればなるほど、この「小さな安全の積み重ね」が、将来のあなたやチームメンバーの夜勤を救うことになります。
JavaScriptの自由な世界を楽しみつつ、大切なデータにはちょっとだけ厳格な「お守り」を持たせてあげてくださいね。それでは、また次回のモダンフロントエンドの旅でお会いしましょう!

コメント