Javascript オブジェクトの浅い/深いコピー

## Javascript オブジェクトの浅い/深いコピー

### 概要
Javascript でオブジェクトをコピーしたい場合、Object.assign()メソッドが使えます。Object.assign()メソッドはいわゆる浅いコピーとなっていて、注意が必要です。
浅いコピーで何が問題になるのかを説明します。

### 浅いコピーの問題点
ゲーム制作を例に取り上げましょう。
出現するモンスターの特徴をJavascript のオブジェクトで管理しているとします。
モンスターの名前はスライム、色は青です。モンスターには武器を複数持たせることができて、今回はダメージが100の剣を装備しています。

``` js
const monster = {
  name: "スライム",
  color: "青", 
  weapons:[
    {name:"剣", damage:100}
  ]
}
```
<img src="https://qiita-image-store.s3.amazonaws.com/0/134076/12ab6192-40e6-f3fb-6809-23a54007c082.png" alt="figure1.png" height="150px" />


絵柄を使い回すこともありますので、コピーして闇スライムをつくりましょう。

```js
const monster = {
  name: "スライム",
  color: "青", 
  weapons:[
    {name:"剣", damage:100}
  ]
}
const darkMonster = Object.assign({},monster);
darkMonster.name = "闇スライム";
darkMonster.color = "黒";

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{"name":"剣","damage":100}]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{"name":"剣","damage":100}]
}
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/134076/c9b95c01-1e46-df38-df01-3e6132da9020.png" alt="figure2.png" height="150px" />

闇スライムは魔法を使えそうなので、魔法の杖を装備させます。

```js
const monster = {
  name: "スライム",
  color: "青", 
  weapons:[
    {name:"剣", damage:100}
  ]
}
// 浅いコピー
const darkMonster = Object.assign({},monster);
darkMonster.name = "闇スライム";
darkMonster.color = "黒";
darkMonster.weapons[0] = {name:"魔法の杖", damage: 200}; // 追加

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{name:"魔法の杖", damage: 200};]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{name:"魔法の杖", damage: 200};]
}
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/134076/01e3b8f9-d227-3ad1-60e8-acb2b014d0b2.png" alt="figure2.png" height="150px" />

問題発生です！！！
闇スライムの武器を変更したら、つられて通常スライムの武器まで変更されてしまいました。
通常スライムの武器は変更していないにもかかわらず、です。
これはObject.assign()が浅いコピーであることが原因です。
浅いコピーでは、オブジェクトが保有する直接の値しかコピーされません。
nameプロパティ、colorプロパティはそのままコピーされますが、
weaponsプロパティは配列への参照を持っているだけなので、参照がコピーされます。
(weaponsプロパティの値は、結局はスライムも闇スライムも同じ場所を見ることになる)
要するにObject.assing()でのコピーは、オブジェクトの浅い部分までは面倒みるけど、深いところまではいちいちコピーしないからね！ということなのです。

### 深いコピー
深いコピーをするためにはJSON.stringify() メソッドと、JSON.parse() メソッドを上手く使います。JSON.stringify() は、javasctiptのオブジェクトをJSON文字列に変換するメソッドで、JSON.parse() はその逆です。

```js
const monster = {
  name: "スライム",
  color: "青", 
  weapons:[
    {name:"剣", damage:100}
  ]
}
// 深いコピー
const darkMonster = JSON.parse(JSON.stringify(monster));
darkMonster.name = "闇スライム";
darkMonster.color = "黒";
darkMonster.weapons[0] = {name:"魔法の杖", damage: 200}; // 追加

console.log(monster);
/*
{
  "name":"スライム",
  "color":"青",
  "weapons":[{name:"剣", damage:100};]
}
*/
console.log(darkMonster);
/*
{
  "name":"闇スライム",
  "color":"黒",
  "weapons":[{name:"魔法の杖", damage: 200};]
}

```

<img src="https://qiita-image-store.s3.amazonaws.com/0/134076/6b127882-4b44-668c-c87e-8ae24dacb94d.png" alt="figure4.png" height="150px" />

これで上手くいきました！

### lodash で深いコピー
lodash は、便利な関数を集めたライブラリです。実際にJavascript のプロジェクトにはよく導入されている気がします。lodash で深いコピーをするには以下のようになります。

```js
// 深いコピー
const darkMonster = _.cloneDeep(monster);
```

### まとめ
* Object.assign() は浅いコピー(直接保有の値までしかコピーされない)
* 深いコピーをするにはJSON.stringify() とJSON.parce() を上手く使う
* 深いコピーをするにはlodash を使う