JavaScript | 配列・オブジェクト:ネスト構造の扱い – ネストオブジェクト

JavaScript JavaScript
スポンサーリンク
  1. ネストオブジェクトとは何か
  2. ネストへのアクセス(ドット・ブラケット・オプショナルチェーン)
    1. ドット記法で固定キーを辿る
    2. ブラケット記法で動的キー・配列インデックスを使う
    3. オプショナルチェーン(?.)で“欠損でも落ちない”
  3. ネストの更新(非破壊更新が基本)
    1. 直接代入は“共有参照”に注意
    2. スプレッドで部分更新(浅いコピー+階層ごとの独立)
    3. 配列要素の更新(インデックスと再構築)
  4. 浅いコピーと深いコピー(どこまで独立させるか)
    1. 浅いコピーは“外側だけ”、入れ子は参照共有
    2. 深いコピーが必要な場合(structuredClone)
  5. 反復(ネストを列挙して処理する)
    1. Object.entries と for…of でキー・値を同時処理
    2. 配列の反復は for…of / map / filter を使う
    3. ネスト全体の走査(再帰の基本形)
  6. 安全な設計(欠損・存在判定・デフォルト)
    1. “キーがある”と“値がある”は別物
    2. オプショナルチェーン+既定値で堅牢に
    3. デフォルトの適用(後勝ちの合成)
  7. JSON とネスト(送受信・保存時の注意)
    1. stringify/parse で文字列⇄データ
  8. 実践レシピ(ネストでよくある操作)
    1. テーブル行をキーで差し替える(配列のオブジェクト)
    2. ネストから安全に取り出して既定値を付ける
    3. 深いパスの存在チェック
  9. よくある落とし穴(重要ポイントの深掘り)
    1. 中間が undefined で落ちる
    2. 浅いコピーの過信
    3. for…in を配列に使う
    4. JSON 経由で型が失われる
  10. まとめ

ネストオブジェクトとは何か

ネストオブジェクトは「オブジェクトや配列の中に、さらにオブジェクトや配列が入っている構造」です。現実世界のデータ(ユーザー→住所→郵便番号、注文→明細の配列→各行のSKUなど)を自然に表現できます。ここが重要です:取り扱いは“辿る(アクセス)”“更新する”“安全に扱う(欠損対応)”“反復する(列挙)”の4視点を押さえると迷いません。

const order = {
  id: 100,
  customer: { id: 1, name: "Alice", address: { city: "Tokyo", zip: "100-0001" } },
  items: [{ sku: "A1", qty: 2 }, { sku: "B5", qty: 1 }]
};
JavaScript

ネストへのアクセス(ドット・ブラケット・オプショナルチェーン)

ドット記法で固定キーを辿る

order.customer.name;           // "Alice"
order.customer.address.city;   // "Tokyo"
JavaScript

静的(英数字などの識別子)キーには最短・最も読みやすい方法です。

ブラケット記法で動的キー・配列インデックスを使う

const field = "zip";
order.customer.address[field]; // "100-0001"
order.items[0].sku;            // "A1"
JavaScript

キーが変数・式・特殊文字(スペース・日本語・記号)を含むときはブラケット記法が必須です。

オプショナルチェーン(?.)で“欠損でも落ちない”

const zip = order.customer?.address?.zip ?? "(unknown)";
JavaScript

ここが重要です:?. は存在すれば辿り、無ければ undefined を返します。?? と組み合わせて既定値を与えると、TypeError を避けつつ読みやすいコードになります。


ネストの更新(非破壊更新が基本)

直接代入は“共有参照”に注意

order.customer.address.city = "Osaka"; // 元データを直接変更(共有環境で危険)
JavaScript

共有される状態(UI・ストアなど)では、元を壊さず“新インスタンスを返す”のが鉄則です。

スプレッドで部分更新(浅いコピー+階層ごとの独立)

const next = {
  ...order,
  customer: {
    ...order.customer,
    address: { ...order.customer.address, city: "Osaka" }
  }
};
JavaScript

ここが重要です:浅いコピーを重ね、更新したい階層だけもう一段スプレッドします。これが実務の最強パターンで、変更検知が安定しバグも減ります。

配列要素の更新(インデックスと再構築)

const i = 1;
const nextItems = order.items.map((row, idx) =>
  idx === i ? { ...row, qty: row.qty + 1 } : row
);
const nextOrder = { ...order, items: nextItems };
JavaScript

map で置き換える要素を選び、スプレッドでその要素を非破壊更新します。


浅いコピーと深いコピー(どこまで独立させるか)

浅いコピーは“外側だけ”、入れ子は参照共有

const shallow = { ...order };
shallow.customer.name = "Bob";
order.customer.name; // "Bob"(内側は同じ参照)
JavaScript

浅いコピーは軽量ですが、入れ子まで独立はしません。更新階層でさらにスプレッドする必要があります。

深いコピーが必要な場合(structuredClone)

const deep = structuredClone(order);
deep.customer.name = "Carol";
order.customer.name; // "Alice"(完全に独立)
JavaScript

ここが重要です:型が複雑(Date/Map/Set/TypedArrayなど)や循環参照がある場合は structuredClone が安全。JSON 経由は関数・undefined・Symbol・複雑な型が失われるため“純粋データ限定”です。


反復(ネストを列挙して処理する)

Object.entries と for…of でキー・値を同時処理

const user = { id: 1, name: "Alice" };
for (const [k, v] of Object.entries(user)) {
  // k と v を使って整形・検証
}
JavaScript

自前の列挙可能なキーだけが返るため安全。ネストを辿るときは再帰や明示的な経路指定を使います。

配列の反復は for…of / map / filter を使う

for (const item of order.items) {
  console.log(item.sku, item.qty);
}
const heavy = order.items.filter(it => it.qty >= 2);
JavaScript

配列に for…in は使わない方が安全です(順序保証が弱く、追加プロパティも回り得ます)。

ネスト全体の走査(再帰の基本形)

function walk(obj, visit) {
  if (obj && typeof obj === "object") {
    for (const [k, v] of Object.entries(obj)) {
      visit(k, v);
      walk(v, visit);
    }
  }
}
walk(order, (k, v) => { /* 各ノード処理 */ });
JavaScript

ここが重要です:再帰は“オブジェクトか配列か”を見て分岐し、必要なら訪問履歴で循環参照を防ぎます。


安全な設計(欠損・存在判定・デフォルト)

“キーがある”と“値がある”は別物

const o = { a: undefined, b: 0 };
Object.hasOwn(o, "a"); // true(キーはある)
o.a === undefined;     // true(値は未定義)
JavaScript

存在判定は in / Object.hasOwn、値の確認は === undefined / === null を使い分けます。

オプショナルチェーン+既定値で堅牢に

const city = order.customer?.address?.city ?? "N/A";
JavaScript

ネスト欠損で落ちないのが最重要。分割代入では ?. が使えないため、受け皿に {} を置くパターンを使います。

const { customer: { address: { city = "N/A" } = {} } = {} } = order;
JavaScript

デフォルトの適用(後勝ちの合成)

const defaults = { customer: { address: { city: "Tokyo", zip: "000-0000" } } };
const merged = {
  ...defaults,
  customer: {
    ...defaults.customer,
    address: { ...defaults.customer.address, ...order.customer?.address }
  }
};
JavaScript

既定値→実データの順に“後勝ち”で埋めると、欠損を安全に補えます。


JSON とネスト(送受信・保存時の注意)

stringify/parse で文字列⇄データ

const json = JSON.stringify(order);  // 送信・保存
const back = JSON.parse(json);       // 復元
JavaScript

ここが重要です:JSON は関数・undefined・Symbol・Date・Map/Setを保持できません。Date は文字列化されるため、parse 後に reviver で復元します。

const obj = JSON.parse(json, (k, v) => k === "date" ? new Date(v) : v);
JavaScript

実践レシピ(ネストでよくある操作)

テーブル行をキーで差し替える(配列のオブジェクト)

function updateBySku(order, sku, patch) {
  const items = order.items.map(r => r.sku === sku ? { ...r, ...patch } : r);
  return { ...order, items };
}
JavaScript

ネストから安全に取り出して既定値を付ける

function getZip(order) {
  return order.customer?.address?.zip ?? "(unknown)";
}
JavaScript

深いパスの存在チェック

function hasPath(o, path) {
  let cur = o;
  for (const key of path) {
    if (cur == null || !(key in cur)) return false;
    cur = cur[key];
  }
  return true;
}
hasPath(order, ["customer", "address", "zip"]); // true/false
JavaScript

よくある落とし穴(重要ポイントの深掘り)

中間が undefined で落ちる

// NG: order.customer.address.city (customerが無いとTypeError)
// OK:
order.customer?.address?.city;
JavaScript

浅いコピーの過信

浅いコピー { ...obj } は内側が共有。入れ子を更新するときは“その階層でもスプレッド”が必要です。

for…in を配列に使う

順序・穴・追加プロパティの問題で誤動作の温床。配列は for…of/map/filter を使うのが原則です。

JSON 経由で型が失われる

関数・undefined・Date・Map/Set は消えたり文字列化されます。深いコピーや永続化で型を維持したいなら structuredClone や型復元(reviver)を併用します。


まとめ

ネストオブジェクトは「現実の階層データ」を表現する最強の入れ物です。アクセスはドット/ブラケットを使い分け、欠損には ?.+?? で堅牢に。更新は“非破壊”を徹底し、浅いコピーを重ねて必要階層だけさらにスプレッドする。反復は Object.entries と配列メソッドを使い、存在判定・既定値・JSONの限界を理解する。これを押さえれば、初心者でも複雑なネスト構造を短く明快に、しかも安全に扱えるようになります。

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