JavaScriptでオブジェクトをコピーする方法:シャローコピーとディープコピーの違いを解説
プログラミング言語を問わず、オブジェクトをコピーする手法には主に2種類あります。それが「シャローコピー(浅いコピー)」と「ディープコピー(深いコピー)」です。この2つの違いを正しく理解することは、意図しないバグを防ぐうえで非常に重要です。
シャローコピー(浅いコピー)とは
シャローコピーは、必要最小限の範囲だけを複製する方法です。コレクション(オブジェクトや配列など)のシャローコピーでは、その構造のみが複製され、内部の要素までは複製されません。そのため、コピー元とコピー先のオブジェクトは、ネストされた内部の要素への参照を共有することになります。
JavaScriptでは、Object.assign() やスプレッド構文({ ...obj })を使ってシャローコピーを作成できます。
サンプルコード
let innerObj = {
a: 'b',
c: 'd'
}
let obj = {
x: "test",
y: innerObj
}
// Object.assign()でシャローコピーを作成
let copyObj = Object.assign({}, obj);
// 内部オブジェクトの値を変更
innerObj.a = "test"
console.log(obj)
console.log(copyObj)
実行結果
{ x: 'test', y: { a: 'test', c: 'd' } }
{ x: 'test', y: { a: 'test', c: 'd' } }
この出力から分かるように、コピー元の innerObj.a を変更すると、コピー先である copyObj の内容も同じように変わってしまいます。これは、両者が同じ内部オブジェクトへの参照を共有しているためです。
つまり、シャローコピーは再帰的にクローンを作成せず、最上位レベルのプロパティだけをコピーします。
ディープコピー(深いコピー)とは
ディープコピーは、すべてを丸ごと複製する方法です。コレクションのディープコピーでは、元のコレクションに含まれる全要素が完全にクローンされるため、コピー元とコピー先は互いに独立したオブジェクトになります。
従来のJavaScriptでは、JSON.parse(JSON.stringify()) を組み合わせる方法がよく使われてきました。
サンプルコード
let innerObj = {
a: 'b',
c: 'd'
}
let obj = {
x: "test",
y: innerObj
}
// JSON変換を使ってディープコピーを作成
let copyObj = JSON.parse(JSON.stringify(obj))
// 内部オブジェクトの値を変更
innerObj.a = "test"
console.log(obj)
console.log(copyObj)
実行結果
{ x: 'test', y: { a: 'test', c: 'd' } }
{ x: 'test', y: { a: 'b', c: 'd' } }
今回は、コピー元の変更がコピー先に影響していません。copyObj の y プロパティは、元の innerObj とは独立した新しいオブジェクトだからです。
モダンな選択肢:structuredClone()
なお、JSON.parse(JSON.stringify()) の手法には、関数、undefined、Symbol、循環参照などを扱えないという制約があります。現在では、こうした問題を解決する structuredClone() メソッドが標準機能として利用可能です。
// structuredClone()によるディープコピー
let copyObj = structuredClone(obj)
用途に応じて適切なコピー方法を選び、参照の共有による思わぬ副作用を避けるようにしましょう。
-
【図解付き】JavaScriptにおけるオブジェクトの等価性をわかりやすく解説
JavaScriptでは、文字列・数値・真偽値などのプリミティブ型はその「値」に基づいて比較されます。一方、オブジェクト(ネイティブオブジェクトでも独自に定義したカスタムオブジェクトでも)は「参照」に基づいて比較されます。 「参照による比較」とは、複数のオブジェクトがメモリ上の同じ場所を指しているかどうかを判定するという意味です。つまり、プロパティの中身が完全に一致していても、別々に生成された2つのオブジェクトは等しいとみなされません。 押さえておきたいポイント プリミティブ型: 値が同じであれば == や === の比較で true になる オブジェクト型: 同じインスタンス(同一の参照
-
JavaScriptでオブジェクトにreduceメソッドを適用する方法
JavaScriptのreduce()メソッドは本来配列向けのメソッドのため、オブジェクトにそのまま適用することはできません。そこで、Object.values()メソッドを使ってオブジェクトの値を配列として取り出し、その配列に対してreduce()を適用します。 以下は、JavaScriptでオブジェクトの値にreduceメソッドを適用するコード例です。 コード例 <!DOCTYPE html> <html lang=en> <head> <meta charset=UTF-8 /> <meta name=viewport content