JavaScript

最終更新日:
公開日:

レシピ

オブジェクト

オブジェクトを複製する

すでにあるオブジェクトから別のオブジェクトを複製する方法について解説します。

この記事のポイント

  • オブジェクトの複製は参照渡しに注意する
  • オブジェクトが入れ子(階層状態)のときはJSONオブジェクトのメソッドを使って複製する

目次

オブジェクトを複製する

すでにあるオブジェクトを複製したいときはassignメソッドを使います。

以下の例では変数data1に入っているオブジェクトを変数data2に複製します。

コード例

let data1 = {
  id: 15001,
  name: "Mike"
};

let data2 = null;

data2 = Object.assign( {}, data1);

data2.id = 15002;
data2.name = 'Lili';

console.log(data1); // {id: 15001, name: "Mike"}
console.log(data2); // {id: 15002, name: "Lili"}

assignメソッドには第1パラメータに空のオブジェクトを渡して、第2パラメータに複製したい元のオブジェクトを渡します。
今回は変数data1に入っているオブジェクトを複製したいので、上記のようにdata1は第2パラメータに指定して、空のオブジェクトを第1パラメータに指定します。

assignメソッドは複製したオブジェクトを戻り値にするため、変数data2で受け取ります。

複製したいオブジェクトがオブジェクトの入れ子構造になっている場合の対応方法は後述しますが、まずは変数の代入ではオブジェクトを複製することができない例を解説します。

変数の代入ではオブジェクトを複製できない

assignメソッドを使わずに、変数の代入をするとオブジェクトは参照渡しになってしまうため複製することはできません。

以下の例では変数data1に入っているオブジェクトをそのまま変数data2に代入した後に、変数data2の各プロパティに値を設定しています。

コード例

let data1 = {
  id: 15001,
  name: "Mike"
};

let data2 = data1;

data2.id = 15002;
data2.name = 'Jesica';

console.log(data1); // {id: 15002, name: "Jesica"}
console.log(data2); // {id: 15002, name: "Jesica"}

変数data1data2の値を出力してみると、変数data2のプロパティに設定した値がdata1のオブジェクトにも反映されてしまっていることが分かります。

オブジェクトが入れ子になっている場合の複製

オブジェクトのプロパティが以下のように入れ子になっている場合は、assignメソッドによる複製は注意が必要です。

例えば、以下のようにオブジェクトのプロパティの値としてオブジェクトが入っているデータを複製して、複製した後のオブジェクトのプロパティに新しい値を設定してみます。

コード例

let data1 = {
  id: 15001,
  name: "Mike",
  item: {
    id: 10001,
    name: "Pen"
  }
};

let data2 = null;

data2 = Object.assign( {}, data1);

data2.id = 15002;
data2.name = 'Lili';
data2.item.id = 10002;
data2.item.name = "Book";

console.log(data1);
// id: 15001
// name: "Mike"
// item: {id: 10002, name: "Book"}

console.log(data2);
// id: 15002
// name: "Lili"
// item: {id: 10002, name: "Book"}

出力した値を確認すると、idプロパティnameプロパティは問題ありませんが、itempプロパティの値がdata1のものも更新されてしまっていることが分かります。
この現象はオブジェクトの参照が解除されていないために発生しますが、JSONオブジェクトstringifyメソッドparseメソッドを使って参照を解除してから複製することで解消できます。

コード例

let data1 = {
  id: 15001,
  name: "Mike",
  item: {
    id: 10001,
    name: "Pen"
  }
};

let data2 = null;

data2 = JSON.parse(JSON.stringify(data1));

data2.id = 15002;
data2.name = 'Lili';
data2.item.id = 10002;
data2.item.name = "Book";

console.log(data1);
// id: 15001
// name: "Mike"
// item: {id: 10001, name: "Pen"}

console.log(data2);
// id: 15002
// name: "Lili"
// item: {id: 10002, name: "Book"}

2つのオブジェクトのitemプロパティが正常に設定されました。

以下のコードを使って、「JSON.parse(JSON.stringify(data1))」でどんなことが起こっているか確認していきます。

コード例

let data1 = {
  id: 15001,
  name: "Mike",
  item: {
    id: 10001,
    name: "Pen"
  }
};

// stringifyメソッド
let temp = JSON.stringify(data1);
console.log(temp);
// {"id":15001,"name":"Mike","item":{"id":10001,"name":"Pen"}}

// parseメソッド
console.log(JSON.parse(temp));
// id: 15001
// name: "Mike"
// item: {id: 10001, name: "Pen"}

まず、「JSON.stringify(data1);」の箇所でstringifyメソッドを実行することでオブジェクトをJSON形式のテキストに変換します。
この時点ですでに、変数data1に入っているオブジェクトとは参照が切断されます。

続いて、parseメソッドを実行して変数tempに入っているJSON形式のテキストを解析し、JavaScriptのオブジェクトを作成します。
プレーンなテキストをオブジェクトに変換するイメージです。

以上の2つの処理から、stringifyメソッドによるJSON変換によってオブジェクトの参照を解除し、全く新しい別のオブジェクトとして作成し直すことで参照渡しを避けたオブジェクトの複製をすることができます。