配列をコピーしたつもりが、元のデータも変わる|スプレッド構文は一段しかコピーしない
スプレッド構文でコピーしたつもりでも、複製先を書き換えると元のデータまで変わることがある。
一段しかコピーしないと知っておくと、直し方が変わる。
設定画面を作っている場面を考える。
ユーザーごとに、絞り込み条件のテンプレートを配りたい。
もとになるオブジェクトをひとつ用意して、それを人数ぶんコピーして、一人ひとりに渡す。
ところが、Aさんの画面で条件をひとつ変えただけで、Bさんの画面まで同じように変わる。
保存もしていないのに、である。
JavaScript でよく踏む罠のひとつだ。
コピーしたつもりのところで、コピーされていない
たとえばこういうコードだ。
const base = { filters: ["camp", "outdoor"], sort: "new" };
const userA = { ...base };
userA.filters.push("gear");
スプレッド構文で複製したのだから、userA だけが変わるように見える。
実際には base.filters にも "gear" が増える。
MDN の説明にはこうある。
「スプレッド構文は、配列をコピーする際に効果的に1レベル深くコピーします。したがって、多次元配列のコピーには適していない可能性があります」
オブジェクトの場合も同じで、「単一のスプレッドは、元のオブジェクトのシャローコピーを作成します」と書いてある。
1レベルだけ、という言葉は読み流しやすい。
自分のオブジェクトで引っかかるまでは、気にならない一文だと思う。
1レベルの外側にあるものは、コピーではなく「同じもの」
{ ...base } がコピーするのは、filters という「箱」ではなく「箱の場所」だ。
箱の中身(配列そのもの)は複製されず、base.filters と userA.filters は同じ配列を指したままになる。
MDN の多次元配列の例が、まさにこの状態を示している。
const a = [[1], [2], [3]];
const b = [...a];
b.shift().shift();
// 1
「しまった。'a' も影響を受けてしまった」
と MDN 自身が書いている。
それだけ多くの人が踏む罠なのだと思う。
文字列や数値のような「値そのもの」を並べた配列なら、スプレッドでのコピーは期待どおりに動く。
要素がオブジェクトや配列になった瞬間、話が変わる。
上の例の base.filters は配列なので、まさにその境目にある。
直すなら structuredClone
いちばん手っ取り早い直し方は、ネストしている部分ごと複製することだ。
const userA = structuredClone(base);
これで base と userA は完全に独立する。
filters を push しても、base 側は変わらない。
以前は JSON.parse(JSON.stringify(base)) という書き方をよく見かけた。
動くのだが、関数や undefined、Date オブジェクトが混ざっていると、コピーの途中で形が変わったり消えたりする。
structuredClone はブラウザの組み込み機能で、Date なども形を保ったまま複製できる。
関数が入っているとエラーになるので、そこだけは別に扱う。
対応状況を確認したいときは、この API のページ自体を見るのが早い。
それでも全部を深くコピーすればいいわけではない
なんでも structuredClone で複製すれば安全かというと、そうとも言い切れない。
設定オブジェクトの中に、あえて共有しておきたい参照が混ざっていることがある。
例えばアイコンの画像オブジェクトのような「変更されない前提のもの」まで複製すると、無駄にメモリを食うし、同じ画像を指しているという前提が崩れて、後から見分けがつきにくくなる。
「この先で書き換えるかもしれない部分だけ複製する」という発想のほうが素直だと思う。
上の例なら filters と sort だけが書き換え対象なので、そこだけ複製すれば足りる。
疑うタイミングを一つ決めておく
複製してから片方だけ変えたつもりなのに両方変わる、という症状を見たら、まずここを疑うといい。
複製に使ったのがスプレッド構文か Object.assign で、かつ中身にオブジェクトや配列が入っていないか。
入っていたら、そこだけ浅いコピーで止まっている。
並べ替えで数字の順番がおかしくなる話を前に書いたけれど(並び替えたら、100が2より先に来た。JavaScriptは数字を数字として見ていなかった)、あれもこれも「見た目どおりに動いていると思い込みやすい場所」が原因だ。
JavaScript は親切に動くぶん、どこまでが自動でどこからが自分の思い込みか、たまに見えなくなる。
出典
※ 内容は2026年9月7日時点で各ページを確認したものです。
ほかにも書いています
受託・note・スタンプこういう仕組みを作る相談を受けています
毎日の更新・データ集め・集計の自動化/3万円〜収益がいくらだったか、どの施策が外れたか
note / 個人の視点で掘り下げていますスタンプラリーに挑戦する
行った場所は、現地チェックインで記録に残せます※受託の相談とnoteは、運営者が個人で受けている・書いているものです。
