JavaScript でオブジェクトをディープクローンする

Harshit Jindal 2023年10月12日
  1. 浅いコピーと深いコピー
  2. JavaScript の浅いコピーメソッド
  3. JavaScript のディープコピーメソッド
JavaScript でオブジェクトをディープクローンする

JavaScript はオブジェクトの言語です。ほとんどすべてが JavaScript のオブジェクトです。ブール値、数値、文字列、日付、数学、正規表現、配列、関数、およびオブジェクト自体は、すべてオブジェクトです。これらは、さまざまな属性とメソッドで構成されるキーと値のペアのコレクションです。それらはメモリに直接保存され、参照によってのみコピーできます。変数はオブジェクトを格納しませんが、メモリ内のそのオブジェクトへの参照のみを格納します。したがって、オブジェクト変数をコピーしようとすると、同じオブジェクトへの追加の参照が作成されることになります。この方法はシャローコピーと呼ばれます。元のオブジェクトの変更がそのクローンに影響を与えたくないため、理想的ではありません。これにより、オブジェクトをディープクローンするメソッドが必要になります。このチュートリアルでは、JavaScript でオブジェクトをディープクローンする方法を説明します。

浅いコピーと深いコピー

浅いコピーは、オブジェクトのビット単位のコピーです。作成された新しいオブジェクトは、数値、ブール値、文字列などのプリミティブを正常にコピーしますが、オブジェクトへの参照はコピーしません。参照アドレスのみが、同じオブジェクトを指すポインタになります。元のオブジェクトに加えられた変更は、浅いコピーに反映されます。

一方、ディープコピーは、元のオブジェクトへのアドレス/参照だけでなく、オブジェクト全体をコピーします。作成された新しいオブジェクトは、コピーされたオブジェクトに依存しません。JavaScript には、オブジェクトをコピーするためのさまざまな組み込みメソッドが用意されていますが、ほとんどの場合、浅いコピーがデフォルトの動作です。

JavaScript の浅いコピーメソッド

ディープコピーの間違った方法のいくつかを認識できるように、シャローコピーの方法について簡単に説明します。

JavaScript で Spread 構文を使用してオブジェクトのクローンを浅くする

新しいオブジェクトを作成し、spread 構文を使用してオブジェクト内のオブジェクトのコンテンツを独自のものとして列挙することにより、オブジェクトのクローンを作成できます。正しい方法のように見えますが、データの浅いコピーが作成されます。

const obj = {
  a: 1,
  b: {c: 2}
}

const clone = {...obj};  // creates a shallow copy

obj.b.c = 5;
console.log(clone.b.c);  // outputs 5

上記のコードでは、spread 構文を使用してオブジェクトの浅いコピーを作成します。次に、元のオブジェクトで参照されているオブジェクトのプロパティの 1つを変更し、複製されたオブジェクトでプロパティが変更されていることを示します。

JavaScript で Object.assign() を使用してオブジェクトのクローンを作成する

object.assign() メソッドは、オブジェクトの浅いコピーを新しいオブジェクト変数に割り当てます。ターゲットとソースの 2つの引数を取ります。ターゲットは通常、コピー先の空のオブジェクトを表すために使用される空の括弧のペアです。これはオプションの引数ですが、これを渡すと、元のオブジェクトが変更されないようになります。2 番目の引数は、コピーされるオブジェクトです。

const obj = {
  a: 1,
  b: {c: 2}
}

const clone = Object.assign({}, obj);  // creates a shallow copy

obj.b.c = 5;
console.log(clone.b.c);  // outputs 5

上記のコードでは、Object.assign() を使用してオブジェクトの浅いコピーを作成します。次に、元のオブジェクトで参照されているオブジェクトのプロパティの 1つを変更し、複製されたオブジェクトでプロパティが変更されていることを示します。

JavaScript のディープコピーメソッド

JavaScript で JSON.parse()JSON.stringify() を使用してオブジェクトをディープクローンする

JSON.stringify() は JavaScript オブジェクトを JSON 文字列に変換するために使用され、JSON.parse() は JSON 文字列を JavaScript オブジェクトに変換するために使用されます。JSON.parse()JSON.stringify() にラップして、最初に JavaScript オブジェクトを JSON 文字列に変換し、次にそれを解析してオブジェクトのコピーを取得できます。

var user = {name: 'Harshit', age: 21, Profession: 'Software Engineer'};
let fakeDeepCopy = JSON.parse(JSON.stringify(user));

このメソッドはディープコピーを作成しますが、関数のないオブジェクトに対してのみ作成されます。他の参照オブジェクトと同様に、循環依存に問題があります。コピーされたオブジェクトのプロパティの順序も、元のオブジェクトと異なる場合があります。したがって、このメソッドは、プリミティブデータ型がほとんどない単純なオブジェクトがある場合は優れたトリックですが、現実の世界ではお勧めできません。

JavaScript でネイティブディープクローンを使用してオブジェクトをディープクローンする

Node.js v8 モジュールのシリアル化アルゴリズムを使用して、オブジェクトのディープクローンを作成できます。特定の組み込みデータ型に制限されていますが、複製されたデータ内の参照を保持します。これにより、JSON メソッドでサポートされていなかったいくつかの循環構造と再帰構造をコピーできます。

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

JSON メソッドの文字列化と解析と同じように、オブジェクトを逆シリアル化してからシリアル化します。ただし、循環依存関係は保持され、わずかに優れています。

JavaScript で Lodash ライブラリを使用してオブジェクトをディープクローンする

Lodash ライブラリには、浅いコピーと深いコピーの両方の機能、つまり cloneclonedeep があります。これは、完全なライブラリではなく、必要な関数のみをインポートできる優れたライブラリです。clonedeep メソッドは、値を再帰的にコピーしてから、すべてのオブジェクトの継承を保持し、オブジェクトの真のコピーを作成することで機能します。

const lodashClonedeep = require('lodash.clonedeep');
let obj = {a: 1, b: {c: 2}} let deepClone = lodashClonedeep(obj);

ここでは、lodash から clonedeep 関数をロードし、それを使用してオブジェクトをディープクローンします。これは十分にテストされ、保守されているライブラリですが、Node.js でのみ使用でき、VanillaJavaScript では使用できません。

JavaScript で jQuery extend() メソッドを使用してオブジェクトをディープクローンする

jQuery の .extend() を使用して、オブジェクトを浅くコピーしたり、深くコピーしたりできます。これは、データの損失やデータの破損がない、最も信頼性の高いディープクローニング方法です。その主な機能は、2つ以上のオブジェクトをマージすることです。ただし、オブジェクトのクローンを作成するためにも使用できます。次の引数を取ります:[deep]targetobject1 ..... objectN

  1. [deep]:これはオプションの引数です。その唯一の許可された値は true です。関数に渡されると、関数はオブジェクトのディープコピーを作成します。それ以外の場合は、浅いコピーを形成します。
  2. target:拡張するオブジェクト。マージされたすべてのオブジェクトを受け取ります。
  3. object1, ..., objectN:これらは、新しいオブジェクトにマージ/クローンされるオブジェクトです。
let obj = {a: 1, b: {c: 2}} let shallowClone =
    $.extend({}, obj);                    // creates a shallow copy
let deepClone = $.extend(true, {}, obj);  // creates a deep copy

より明白な解決策は、ソースオブジェクトのすべてのプロパティを繰り返し処理し、それらを新しいオブジェクトに複製することです。上記のすべての方法は、すべての主要なブラウザと互換性があります。

著者: Harshit Jindal
Harshit Jindal avatar Harshit Jindal avatar

Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.

LinkedIn

関連記事 - JavaScript Object