JavaScript(ES6)のスプレッド構文とレスト構文と分割代入が意外と多機能だった

## 背景
- ES6のスプレッド構文（Spread Syntax）とレスト構文（Rest Parameters）について、Rubyの可変長引数みたいなものだと思っていたのだけど、調べてみたら思ったより便利だったことを知ったのでまとめてみた。
- レスト構文を調べてたら関連して分割代入（Destructuring Assignment）にも言及せざるを得なくなったのでついでに追加した。

## スプレッド構文（Spread Syntax）
### Array編
#### 関数の引数に使うパターン
- 引数の定義に使うと、実行時の複数の入力を1つの配列として受け取ることができる。

```javascript
const func = (...args) => {
  console.log(args);
}

func(1, 2, 3); // => [1, 2, 3]
```

- 実行時に配列をスプレッド構文で展開して引数に渡すと、配列内の各要素を関数内で別々の引数として受け取ることができる。

```javascript
const func = (a, b, c) => {
  console.log(a, b, c);
}

const array = [1, 2, 3];
func(...array); // => 1, 2, 3;
```

#### 配列の定義に使うパターン
- 配列を定義する際に別の配列の要素をスプレッド構文で展開すると、各要素を新しい配列の要素として渡すことができる。

```javascript
const array = [1, 2, 3];
const newArray = [...array, 4, 5, 6];
console.log(newArray); // => [1, 2, 3, 4, 5, 6]
```

- 展開する変数が `undefined` や `null` だった場合は例外エラーが発生する。

```javascript
const hoge = undefined;
const fuga = null;

const array1 = [...hoge];
// => Uncaught TypeError: undefined is not iterable

const array2 = [...fuga];
// => Uncaught TypeError: null is not iterable
```

#### 余談
- 文字列も文字の配列のような形で展開することができる。

```javascript
const func = (...args) {
  console.log(args);
}

func(...'abc'); // => ['a', 'b', 'c']
```

### Object編
#### 関数の引数に使うパターン
- 引数のオブジェクトの中で特定のプロパティだけを抜き出すことができる。

```javascript
const callName = ({ name }) => {
  console.log(name);
};

const user = {
  name: 'Takuya';
};

callName(user); // 'Takuya'
```

- さらに深い階層を展開することもできるが、ここまで書くとちょっと可読性が下がる気がしている。

```javascript
const callName = ({ user: { name } }) => {
  console.log(name);
};

const params = {
  user: {
    name: 'Katsuya',
  };
};

callName(params); // => 'Katsuya'
```

#### オブジェクトの定義に使うパターン
- オブジェクトをプロパティと値の組に分解してマージできる。
- **プロパティ名がかぶった場合は後から書いた方が優先される。**

```javascript
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { a: 10, d: 4, e: 5 };

const clonedObj = { ...obj1 };
console.log(clonedObj); // => { a: 1, b: 2, c: 3 }

const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // => { a: 10, b: 2, c: 3, d: 4, e: 5 }
```

- React/ReduxのReducer内でオブジェクトの中身を書き換える際とかによく見かける。

```javascript
const updateName = (state, user) => {
  return { ...state, user };
}

const initialState = { id: 1, name: 'Nobuto' };
const newName = 'Shintaro';
const newState = updateName(initialState, newName);

console.log(newState); // => { id: 1, name: 'Shintaro' }
```

- ちなみに展開する変数が `undefined` や `null` だった場合は何も起きない。

```javascript
const hoge = undefined;
const fuga = null;

const obj1 = { ...hoge }; // => {}
const obj2 = { ...fuga }; // => {}
```

## レスト構文（Rest Parameters）
### Array編
#### 関数の引数に使うパターン
- 指定した引数より多くの引数が実行時に渡された場合、残りの引数をまとめて1つの配列として受け取れる。
- この辺はRubyの可変長引数と一緒。
- 残りの引数さえ使っていれば、指定した引数を使わなくても eslint や tslint で `'xxx' is declared but its value is never read.` のようなエラーは出ない。

```javascript
const func = (a, b, ...other) => {
  console.log(other);
};

func(1, 2, 3, 4, 5); // => [3, 4, 5]
```

#### 配列の展開に使うパターン
- 分割代入との組み合わせで、配列から指定した数の要素だけ抜き出して、残りを1つの配列として受け取ることができる。

```javascript
const array = [1, 2, 3, 4, 5];
const [a, b, c] = array;

console.log(a); // => 1
console.log(b); // => 2
console.log(c); // => [3, 4, 5]
```

### Object編
#### 関数の引数に使うパターン
- 引数の中で特定のプロパティだけを抜き出し、残ったプロパティをまとめて1つのオブジェクトとして受け取ることができる。
- これも残りの引数さえ使っていれば、指定した引数を使わなくても eslint や tslint で `'xxx' is declared but its value is never read.` のようなエラーは出ない。

```javascript
const func = ({ name, ...otherProps }) => {
  console.log(otherProps);
}

const user = { name: 'Akira', age: 34, instrument: 'Guitar' };
func(user); // => { age: 34, instrument: 'Guitar' }
```

#### オブジェクトの展開に使うパターン
- オブジェクトの中で特定のプロパティだけを抜き出し、残ったプロパティをまとめて1つのオブジェクトとして受け取ることができる。

```javascript
const user = { name: 'Akira', age: 34, instrument: 'Guitar' };
const { name, ...rest } = user;

console.log(name); // => 'Akira'
console.log(rest); // => { age: 34, instrument: 'Guitar' }
```

## 分割代入(Destructuring Assignment)
### Array編
- 配列の要素を抜き出して別の変数に展開したい時、一括で代入することができる。

```javascript
const [hoge, fuga] = [1, 2];
console.log(hoge); => 1
console.log(fuga); => 2

const [foo, bar, baz] = [3, 4];
console.log(baz); // => undefined
```

- 配列の要素数が足りない場合は `undefined` になる

```javascript
const [foo, bar, baz] = [3, 4];
console.log(baz); // => undefined
```

- 最初の方の要素が要らない場合はカンマで無視できる

```javascript
const array = [1, 2, 3, 4, 5];
const [,second] = array;
const [,,third] = array;

console.log(second); // => 2
console.log(third); // => 3
```

- 指定された要素が `undefined` だった場合のデフォルト値を指定できる
- 指定された要素が `null` だった場合は `null` が代入される

```javascript
const [a = 1] = [];
console.log(a); // => 1

const [b = 2] = [undefined];
console.log(b); // => 2

const [c = 3] = [null];
console.log(c); // => null
```

#### 余談
- 文字列も文字の配列のような形で展開することができる。

```javascript
const [a, b, c] = 'JS';
console.log(a); // => 'J'
console.log(b); // => 'S'
console.log(c); // => undefined

const [d, e, ...f] = 'JavaScript';
console.log(d); // => 'J'
console.log(e); // => 'a'
console.log(f); // => ['v', 'a', 'S', 'c', 'r', 'i', 'p', 't']
```

### Object編
- オブジェクトのプロパティ名が一致する変数に一括で代入できる。

```javascript
const user = { age: 28 };
const { age } = user;
console.log(age); // => 28
```

- 変数名を変更することもできる。

```javascript
const user = { age: 28 };
const { age: newAge } = user;
console.log(newAge); // => 28
console.log(age); // => Uncaught ReferenceError: age is not defined
```

- デフォルト値を指定することもできる。

```javascript
const user = { name: 'Riri', gender: 'female' };
const { instrument = 'none' } = user;

console.log(instrument); // => 'none'
```

そろそろ飽きてきたのでこの辺で。ドキュメントを読むと想像以上に色んな書き方があって面白い。

## 参考
- スプレッド構文: [Spread syntax | MDN](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
- レスト構文: [Rest parameters | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters)
- 分割代入: [Destructuring assignment | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)