分割代入の注意点

## はじめに
スプレッド構文はオブジェクトをイミュータブルに扱いたい場面で活躍します。Reduxでは、よく使われる構文の一つかなと思います。既存のソースを見ても、Reducerを作る際にスプレッド構分で書かかれることが多いと思うので、その利用方法と注意点のまとめです。

#### 対象読者
- JS初学者
- Redux初学者

##スプレッド構文とは

MDNからの引用です。

>配列式や文字列などの反復可能オブジェクトを、0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、0 個以上のキーと値のペア (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。

[MDN スプレッド構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)


実際の利用方法を見てみましょう。

#### オブジェクトで利用する

既存のReducerの記述を見ると、`Object.assign()`で書かれてるものも見かけますが、以下のような書き方で置き換えできるかなと思います。

```javascript

const favMusic = {
  genre: "rock",
  country: "uk"
}

// ① オブジェクトをクローンする
const clone_favMusiv = {
  ...favMusic
} // ▶︎　{ genre: 'rock', country: 'uk' }

// ② オブジェクトをマージして新たなオブジェクトを生成する
const merge_favMusiv = {
  ...favMusic,
  ...{age: "90s", genre_detail: "alternative"}
} // ▶︎　{ genre: 'rock', country: 'uk', age: '90s', genre_detail: 'alternative' }

// ③ プロパティを追加して新たなオブジェクトを生成する
const add_to_clone_favMusiv = {
  ...favMusic,
  age: "90s"
} // ▶︎　{ genre: 'rock', country: 'uk', age: '90s' }
```

#### 配列で利用する

```javascript

const hobby = ["running", "game", "camp"];

// ① 配列をクローンする
const clone_hobby = [...hobby]; // ▶︎　["running", "game", "camp"]

// ② 複数の配列をマージして新たな配列を生成する
const merge_hobby = [...hobby, ...clone_hobby]; // ▶︎　[ 'running', 'game', 'camp', 'running', 'game', 'camp' ]

// ③ 新しい要素を追加して配列を生成する
const add_to_clone_hobby = [...hobby, "fishing", "cooking"]// ▶︎　[ 'running', 'game', 'camp', 'fishing', 'cooking' ]
```



##スプレッド構文の注意点

#### ① 元のオブジェクトに同名プロパティがある場合
同名プロパティを展開した場合、値は上書きされます。

```javascript

const favMusic = {
  genre: "rock",
  country: "uk"
}

// ここでは、genreが上書きされてしまう
const replaceProp_favMusiv = {
  ...favMusic,
  ...{genre: "classic"}
} // ▶︎ { genre: 'classic', country: 'uk' }

```

#### ② ネストしてるオブジェクト/配列

スプレッド構文は、 `shallow_copy`のため、以下のような場合、意図しない実装をしてしまう可能性があるので注意が必要です。ここでは、オブジェクトを例に挙げます。

```javascript

const favMusicDetail = {
  genre: "rock",
  country: "uk",
  artist: {
    name: "The Beatles",
    age: "60s",
  }
}

const shallowCopy = {...favMusicDetail}
// 期待値：False
console.log("参照が異なるか検証", favMusicDetail === shallowCopy); // ▶︎ False

// 期待値：False
console.log("子オブジェクト_参照が異なるか検証", favMusicDetail.artist === shallowCopy.artist); // ▶︎ True
```

上記を確かめると分かるのですが、子オブジェクトは同じ参照であることが分かります。
続いて値を更新してみましょう。

```javascript

// 一方のオブジェクトの値を更新してみます。
favMusicDetail.genre = "classic";
favMusicDetail.artist.age = "70s";

console.log("favMusicDetail: ", favMusicDetail, "shallowCopy: ", shallowCopy);
// 子オブジェクトは参照先が同じなので、両方とも値が更新されている
// artice.ageが書き換わってることに注目
// ⬇️
//favMusicDetail:  {
//  genre: 'classic',
//  country: 'uk',
//  artist: { name: 'The Beatles', age: '70s' }
//} shallowCopy:  {
//  genre: 'rock',
//  country: 'uk',
//  artist: { name: 'The Beatles', age: '70s' }
//}
```

子のオブジェクトは同じ参照なことがわかります。
そのため、ネストしているオブジェクトを扱う場合は、以下の通り、`個別に分割する必要がある`ので注意が必要です。
以下コードを追記して確かめてみると、オブジェクトが正しくクローンされてることが分かると思います。

```javascript
const cloneDetail = {
  ...favMusicDetail,
  artist: {
    ...favMusicDetail.artist, // 分割代入は、shallow_copyのため、個別に分割する必要がある。
  }
}

// 正しくコピーされてる。
favMusicDetail.artist.age = "80s";
console.log("favMusicDetail: ", favMusicDetail, "cloneDetail: ", cloneDetail);
// ⬇️
//favMusicDetail:  {
//  genre: 'classic',
//  country: 'uk',
//  artist: { name: 'The Beatles', age: '80s' }
//} cloneDetail:  {
//  genre: 'classic',
//  country: 'uk',
//  artist: { name: 'The Beatles', age: '70s' }
//}
```