オブジェクト操作設計とは何か
「オブジェクト操作設計」というのは、
単に user.name = "Alice" といった「その場しのぎの代入」を書くのではなく、
- どんな形のオブジェクトにするか(構造・型)
- どこで作って、どこで更新して、どこで読むのか(責務の分け方)
- どうやって「元を壊さず」に扱うか(不変データ)
をあらかじめ決めておくことです。
ES6+ のオブジェクト拡張(スプレッド構文、Object.assign、Object.entries / Object.fromEntries、プロパティ短縮記法、計算されたプロパティ名、メソッド定義省略など)は、この「設計」をきれいに実現するための武器です。
ここが重要です。
文法そのものを知るだけでは不十分で、「どう組み合わせて、安全で読みやすいオブジェクト操作にするか」を意識すると、コードの質が一気に変わります。
データ構造を先に決める(設計のスタート地点)
形を決めるだけで、迷いがかなり減る
まず、「このオブジェクトはどういう形をしているべきか」をハッキリさせます。
例えばユーザー情報なら:
// ユーザー1件の形を決める
// id: number
// name: string
// roles: string[]
// meta: { createdAt: number, updatedAt: number }
const user = {
id: 1,
name: "Alice",
roles: ["user"],
meta: {
createdAt: Date.now(),
updatedAt: Date.now()
}
};
JavaScript「とりあえず突っ込む」ではなく、「このオブジェクトのプロパティはこれとこれ」と決めてからコードを書くと、そのあとスプレッドや Object.assign を使った更新パターンも迷わず書けます。
重要なのは、ネストを含めた「一貫した形」を決めておくことです。
同じ user なのに、ファイルによって形が違う、というのが一番の地獄です。
変更の方針を決める(不変データをベースにする)
原則:「元のオブジェクトは変更せず、新しいオブジェクトを作る」
ES6+ 的な設計では、「不変データ」をベースに考えるのがおすすめです。
例えば設定オブジェクト:
const config = {
theme: "light",
lang: "ja",
debug: false
};
JavaScript「debug を true にしたい」ときに、次の2つの書き方があります。
元を書き換える(可変):
config.debug = true; // 元を直接変更
JavaScript新しいオブジェクトを作る(不変):
const newConfig = { ...config, debug: true };
JavaScriptここが重要です。
設計としては、「基本は後者(不変)」を採用するかどうかをチームで決める。
不変スタイルのメリットは:
- 「いつの間にかどこかで書き換わっている」バグが減る
- 関数が「入力 → 出力」の形になりやすい(再利用しやすい)
- React などのライブラリと相性が良い
初心者のうちは、「外部から渡されたオブジェクトや状態は、原則として直接いじらず、新しいものを返す」と決めてしまうと、安全側に倒せます。
生成・更新・マージのパターンを揃える
生成:プロパティ短縮記法で意図を見せる
「変数からオブジェクトを作る」場面では、プロパティ短縮記法を使うと設計が見やすくなります。
const id = 1;
const name = "Alice";
const roles = ["user"];
const user = { id, name, roles };
JavaScript{ id, name, roles } と書かれていれば、「このオブジェクトはこの3つを持つ」という意図が一目でわかります。
ここで「どんなプロパティを持つべきか」を揃えておくことが、設計の土台になります。
更新:スプレッド構文で「元+差分」の形にする
更新関数は、「元オブジェクトと変更点を受け取り、新しいオブジェクトを返す」形にすると、設計がきれいになります。
function updateUser(user, updates) {
return {
...user,
...updates,
meta: {
...user.meta,
updatedAt: Date.now()
}
};
}
const user1 = {
id: 1,
name: "Alice",
roles: ["user"],
meta: { createdAt: 0, updatedAt: 0 }
};
const user2 = updateUser(user1, { name: "Bob" });
console.log(user1.name); // "Alice"
console.log(user2.name); // "Bob"
JavaScriptここが重要です。
更新の設計として、「元+差分 → 新」というパターンを統一すると、どこから見ても同じリズムでコードが読めます。
マージ:Object.assign / スプレッドの方向と優先度
設定やオプションのマージでは、「どちらを優先するか」を設計で決めておく必要があります。
const defaultOptions = {
method: "GET",
timeout: 3000,
cache: "no-cache"
};
function createOptions(userOptions = {}) {
return {
...defaultOptions, // デフォルト
...userOptions // ユーザーが上書き
};
}
JavaScript{ ...defaultOptions, ...userOptions } という順番にすることで、
「デフォルト → 上書き」という設計がそのままコードに出ます。
この「順番で優先度を表現する」ルールを、一貫して守ることが大事です。
読み出し・安全なアクセスをどう設計するか
分割代入で「必要なものだけ」を明示的に取り出す
関数にオブジェクトを渡すとき、その中から何が必要かを明確にしておくと設計がスッキリします。
function printUser({ id, name, roles }) {
console.log(`(${id}) ${name} [${roles.join(", ")}]`);
}
const user = {
id: 1,
name: "Alice",
roles: ["user", "admin"],
meta: { createdAt: 0 }
};
printUser(user);
JavaScript引数で分割代入をすることで、「この関数は user のうち id, name, roles にだけ関心がある」という設計が、コードに現れます。
ネストしたプロパティへのアクセスと不変更新
ネストが深くなったときの更新も、設計しておかないとすぐカオスになります。
例えば、状態オブジェクト:
const state = {
user: {
id: 1,
name: "Alice"
},
ui: {
theme: "light"
}
};
JavaScriptuser.name を変えたい場合の「不変更新パターン」はこうです。
const newState = {
...state,
user: {
...state.user,
name: "Bob"
}
};
JavaScriptここが重要です。
「外側をスプレッド → 変えたい内側もスプレッド+上書き」というパターンを、
「ネスト1段ごとに繰り返す」と決めておくと、ネストが深くなってもブレません。
Object.entries / fromEntries を使った「変換」の設計
オブジェクトを「配列として変換」→「またオブジェクトに戻す」
設計の中で、「オブジェクトの一部だけを加工したい」という場面も多く出てきます。
このとき Object.entries と Object.fromEntries を使うと、「オブジェクト操作」を「配列操作」として設計できます。
例えば、値を2倍する:
const prices = { apple: 100, orange: 80 };
const doubled = Object.fromEntries(
Object.entries(prices).map(([key, value]) => [key, value * 2])
);
console.log(doubled); // { apple: 200, orange: 160 }
JavaScript重要なのは、「設計としてこの流れを採用するか」です。
- Object.entries でオブジェクト →
[key, value]配列 - 配列として map / filter などで柔軟に加工
- Object.fromEntries でオブジェクトに戻す
「オブジェクトの変換処理はこの3ステップで書く」と決めておくと、
自分のコードも他人のコードも読み解きやすくなります。
小さな「操作関数」に分ける設計
「データの形」ごとに操作関数を用意する
例えばユーザーオブジェクトに対して、こんな操作があるとします。
- 名前を変更する
- ロールを追加する
- アクティブフラグを変える(active: true/false など)
これを全部その場で直接いじるのではなく、
「ユーザー操作関数」として分けておくと設計が綺麗になります。
function renameUser(user, newName) {
return { ...user, name: newName };
}
function addRole(user, role) {
return { ...user, roles: [...user.roles, role] };
}
function setActive(user, active) {
return { ...user, active };
}
JavaScriptこうしておけば、使う側は「設計どおりの操作」しかできません。
const user1 = { id: 1, name: "Alice", roles: ["user"], active: false };
const user2 = renameUser(user1, "Bob");
const user3 = addRole(user2, "admin");
const user4 = setActive(user3, true);
JavaScriptここが重要です。
「オブジェクトの形」と「そのオブジェクトに許される操作」をセットで設計しておく。
これができていると、オブジェクト操作が「バラバラの代入の寄せ集め」ではなく、「意味のあるAPI」になります。
具体例で総仕上げ:ユーザー管理オブジェクトの設計
データ形の定義
// ユーザー1件の形
// id: number
// name: string
// roles: string[]
// active: boolean
// meta: { createdAt: number, updatedAt: number }
function createUser(id, name) {
const now = Date.now();
return {
id,
name,
roles: ["user"],
active: true,
meta: {
createdAt: now,
updatedAt: now
}
};
}
JavaScript操作関数の定義(不変更新)
function renameUser(user, newName) {
return {
...user,
name: newName,
meta: {
...user.meta,
updatedAt: Date.now()
}
};
}
function addRole(user, role) {
if (user.roles.includes(role)) return user;
return {
...user,
roles: [...user.roles, role],
meta: {
...user.meta,
updatedAt: Date.now()
}
};
}
function deactivateUser(user) {
if (!user.active) return user;
return {
...user,
active: false,
meta: {
...user.meta,
updatedAt: Date.now()
}
};
}
JavaScript利用側のコード(読みやすさの確認)
let user = createUser(1, "Alice");
user = renameUser(user, "Bob");
user = addRole(user, "admin");
user = deactivateUser(user);
console.log(user);
JavaScriptここまでくると、「オブジェクト操作設計」が見えてきます。
- 形は
createUserで統一 - 更新は「元+差分 → 新」で不変
- 更新タイミングで
meta.updatedAtを必ず更新(設計ルール) - 利用側は「意味のある操作」のみを呼ぶ
ES6+ のオブジェクト拡張(スプレッド構文・短縮記法など)が、この設計を支えていることがわかると思います。
まとめ
オブジェクト操作設計のカギは、文法そのものよりも、
- オブジェクトの「形」を先に決める
- 変更方針を「不変」をベースに揃える
- 生成・更新・マージのパターンを統一する
- entries / fromEntries で「変換処理」を設計する
- 「形」と「許される操作」をセットで小さな関数として定義する
という「考え方」にあります。
ES6+ のオブジェクト拡張機能は、この考え方をコードに落とし込むための道具です。
スプレッド、プロパティ短縮、計算プロパティ名、Object.assign、Object.entries / Object.fromEntries などを、「設計を支えるための文法」として意識すると、書くコードのレベルが一段上がります。
最初から完璧な設計を目指す必要はありません。
まずはひとつ、「このオブジェクトだけは不変スタイルで操作関数を用意してみる」ところから始めてみてください。そこから「設計する楽しさ」が見えてきます。

