JavaScript | 配列・オブジェクト:配列の基礎 – 配列のコピー方法

JavaScript JavaScript
スポンサーリンク

配列のコピーとは何か

配列のコピーは「元の配列を壊さず、別の配列インスタンスを作る」ことです。ここが重要です:コピーには“浅いコピー”(一次元ぶんだけ独立)と“深いコピー”(入れ子の中身まで独立)があり、目的に応じて使い分けます。参照渡しのまま操作すると、元配列まで意図せず変わるので、コピーはバグ回避の基本手段です。


浅いコピー(一次元だけ独立)

スプレッド構文で最短コピー

const a = [1, 2, 3];
const b = [...a];       // 浅いコピー
b.push(4);
console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3, 4]
JavaScript

ここが重要です:スプレッドは“枠”を複製します。要素が数値や文字列なら十分ですが、要素がオブジェクト・配列だと中身の参照は共有されます。

slice でコピー(古典かつ高速)

const a = [1, 2, 3];
const b = a.slice();    // 全体コピー
JavaScript

ここが重要です:slice() は“元を壊さない”非破壊メソッド。慣れた現場でもよく使われます。

Array.from でコピー+変換にも対応

const a = [1, 2, 3];
const b = Array.from(a);           // そのままコピー
const c = Array.from(a, x => x*2); // コピーしつつ変換
JavaScript

ここが重要です:Array.from(iterable, mapFn) は「取り込み」と「変換」を一度に行えます。初期化・前処理に便利です。

concat で“同じ内容の新配列”を得る

const a = [1, 2, 3];
const b = [].concat(a);  // 浅いコピー
JavaScript

ここが重要です:concat は“新配列を返す”。複数配列の結合とコピーを兼ねられます。


深いコピー(入れ子まで完全に独立)

structuredClone が標準の第一選択

const a = [{ id: 1, tags: ["x"] }, { id: 2 }];
const b = structuredClone(a);
b[0].tags.push("y");
console.log(a[0].tags); // ["x"](元は不変)
JavaScript

ここが重要です:入れ子のオブジェクト・配列まで“別インスタンス”にします。関数や特殊型は扱いに制約がありますが、一般的なデータに強いです。

JSON 経由の深いコピー(制約あり)

const a = [{ id: 1 }, { id: 2 }];
const b = JSON.parse(JSON.stringify(a));
JavaScript

ここが重要です:関数・undefined・Date・Map/Set などが失われるため“データだけ”の場面で限定的に使います。型が落ちることを理解しておく必要があります。


参照共有の落とし穴(浅いコピーの限界)

要素がオブジェクトだと中身は共有される

const a = [{ id: 1 }, { id: 2 }];
const b = [...a];       // 浅いコピー
b[0].id = 999;
console.log(a[0].id); // 999(内側は同じ参照)
JavaScript

ここが重要です:浅いコピーは“配列の枠”だけ。入れ子を独立させたいなら structuredClone、または部分的にスプレッドで要素オブジェクトも複製します。

const a = [{ id: 1, name: "A" }, { id: 2, name: "B" }];
const b = a.map(x => ({ ...x })); // 要素オブジェクトも浅く複製
JavaScript

多次元配列・設定配列の安全なコピー

行ごとにコピー(2次元の浅い独立)

const grid = [
  [1, 2],
  [3, 4]
];
const copy = grid.map(row => row.slice()); // 各行を新配列に
JavaScript

ここが重要です:多次元は“層ごと”にコピーします。完全独立が必要なら structuredClone を選びます。

設定オブジェクト配列の更新(非破壊で置き換え)

const users = [{id:1,name:"A"},{id:2,name:"B"}];
const updated = users.map(u => u.id === 2 ? { ...u, name: "B2" } : u);
JavaScript

ここが重要です:コピーしてから変更する“非破壊更新”は、参照共有の副作用を防ぎ、差分検知(UIの再描画)とも相性が良いです。


実践での選び方(目的別の最短ルート)

とりあえず別インスタンスが欲しい(一次元)

  • スプレッド […] または slice()。読みやすく高速。

コピーしつつ変換したい

  • Array.from(source, mapFn)。初期化と前処理を一度に。

入れ子まで完全に分離したい

  • structuredClone。JSON方式は“型が落ちても良い”場合だけ。

コピーしてから並び替えたい(破壊を避ける)

const sorted = [...a].sort((x,y)=>x-y);
JavaScript

ここが重要です:sort は破壊的なので、必ず“コピーしてから”。元配列を守れます。


よくあるバグの芽と対策

代入で済ませて同じ配列を共有してしまう

const a = [1,2,3];
const b = a; // 共有
JavaScript

対策:必ずコピー([…a], a.slice(), Array.from(a) など)を取ってから操作します。

浅いコピーだと入れ子が連動する

対策:要素オブジェクトもスプレッドで複製する、または structuredClone を使う。局所的な更新なら map+スプレッドが読みやすく安全です。

sort/splice をそのまま使って元を壊す

対策:コピーしてから sort、削除は filter(非破壊)に置き換えるのが定石です。


まとめ

配列コピーの基本は“浅いコピー=枠だけ独立、深いコピー=入れ子まで独立”。一次元ならスプレッド・slice・Array.from が最短で安全。入れ子や設定データの完全分離が必要なら structuredClone を使い、JSON方式は制約を理解した上で限定利用。破壊的メソッドを使う前に“まずコピー”——この習慣を徹底すれば、初心者でも副作用のない読みやすいコードにできます。

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