[...Array(5)]←なんでこれが[undefined × 5]に？

suinさんのこちらの記事の補足的な内容になります。
[JavaScriptで[ 0, 1, 2, 3, 4 ]のような連番の配列を生成する方法](https://qiita.com/suin/items/1b39ce57dd660f12f34b)

JavaScriptで連番の配列を作りたいときこんなふうにできるよ、という記事です。

```JavaScript
[...Array(5)].map((_, i) => i)
// [ 0, 1, 2, 3, 4 ]
```

初見だとちょっと何やってるかわからない…特にわからなかったのが以下の部分。

```JavaScript
[...Array(5)]
```

**なんで配列作って展開してまた配列に詰めなおしてるの…？**

# 元記事の解説

この謎の操作の意図は元記事できちんと説明されています。

>### パート1: Array(5)
これは要素数が5つ(=スロットが5つあるだけ)の空っぽの配列を作る。undefinedが5つ入るわけでない。
`Array(5) //=> [ <5 empty items> ]`

わかります。
`Array()`の機能そのまんまです。

>###パート2: [...Array(5)]の[... ]の部分
要素数が5つある配列をspread operatorにかけて、undefined5つの配列に変形している。
`[...Array(5)] //=> [ undefined, undefined, undefined, undefined, undefined ]`

なるほど、空っぽのスロットを`undefined`で埋めるためだったんですね。
`Array.prototype.map()`は値の入っていない空き枠は処理せずにスルーしてしまうので、何かしらの値を入れておかねばならないってことですね。

でも待って、**なんでこれで空き枠埋まるの？**

# 「この程度説明するまでもない」的な？

この記事のポイントはここです。
**「なぜスプレッド構文[^1]で空き枠を埋められるのか？」**

QiitaでもQiita以外でもこのイディオムを解説している記事は多数ヒットするのですが、空き枠が埋まる理由を説明している記事は見つかりませんでした[^2]。


連番配列作るイディオムがわからんので読解してみたよ、という内容の記事もあったのですが、この点については言及なし。

**なんでみんなスルーするの…**

# なぜスプレッド構文で空き枠を埋められるのか？

みんなが当たり前のようにスプレッド構文をスルーしていく中、ただ一人動作機序が理解できない…

悔しかった私はからくりを調べました。

## スプレッド構文は何をしているのか

[スプレッド構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)は、配列などの反復可能オブジェクトを展開する機能を持ちますが、この機能は[反復処理プロトコル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols)を利用して実現されています。

この反復処理プロトコルは、スプレッド構文やfor-of文など、オブジェクトを反復する場面で使われる共通の[^3]しくみです。

`[...iterableObj]`のような式を例にざっくりと流れを説明すると、

1. まず対象のオブジェクトの`@@iterator`メソッドが呼ばれます。例の場合では`iterableObj[Symbol.iterator]()`のような呼び出しになります。
1. 対象の`@@iterator`メソッドは反復子と呼ばれるオブジェクトを返します。
1. 反復子は`next`メソッドを実装しています。`反復子.next()`という呼び出しを繰り返すことで対象のオブジェクトが反復されます。
1. `next`メソッドは、呼び出されるたびに`value`と`done`というプロパティを持つオブジェクトを返します。`value`は反復子によって返される具体的な値で、`done`は反復が完了したかを示す真偽値です。`done`が`true`になったら反復は終了します。
1. 最終的に、`done`が`true`になるまでの各`value`を要素とする配列が返されます。

…という感じ。

ちょっとややこしいですが、ともあれスプレッド構文はこういう仕組みのもとで動いているわけです。

## `[ 0, 1, 2, 3, 4 ]`を手動で反復してみる

スプレッド構文の内部動作を考えるため、まずは手動で`[ 0, 1, 2, 3, 4 ]`を反復してみます。

まず`@@iterator`メソッドを呼んで反復子をゲットしましょう。

```JavaScript
const arr = [ 0, 1, 2, 3, 4 ]
const iter = arr[Symbol.iterator]()
```

ではこの反復子の`next`メソッドを呼びます。

```JavaScript
iter.next() //=> { value: 0, done: false }
iter.next() //=> { value: 1, done: false }
iter.next() //=> { value: 2, done: false }
iter.next() //=> { value: 3, done: false }
iter.next() //=> { value: 4, done: false }
iter.next() //=> { value: undefined, done: true }
```

`done`が`true`になったので反復は終了。実際に反復で返された値は`value`を参照すればOK。
ややこしく感じたわりに意外とシンプルですね。

## 問題の`Array(5)`を手動で反復

それでは本丸の`Array(5)`の攻略です。
先ほどと同じ要領で反復してやりましょう。

```JavaScript
const arr = Array(5)
const iter = arr[Symbol.iterator]()
iter.next() //=> { value: undefined, done: false }
iter.next() //=> { value: undefined, done: false }
iter.next() //=> { value: undefined, done: false }
iter.next() //=> { value: undefined, done: false }
iter.next() //=> { value: undefined, done: false }
iter.next() //=> { value: undefined, done: true }
```

出た、`undefined`だ！
空き枠の部分の`value`が`undefined`として取得されています！

`Array(5)`をスプレッド構文にかけたとき、この`undefined`が要素になっていたんですね。

```JavaScript
[...Array(5)] //=> [ undefined, undefined, undefined, undefined, undefined ]
```

## その`undefined`はどこから？

じゃあなんで空き枠に対応する`value`が`undefined`として返されるの？
スロットは空っぽで何の値も入ってないのに？

JavaScriptでは、返す値がそもそもないときは便宜上`undefined`を返すことが多いので、たぶん今回も同じノリなんでしょう。

…というふわふわした結論では気持ち悪かったので、仕様書を読んでみました。

[仕様書](https://tc39.es/ecma262/#sec-createarrayiterator)によると、配列の反復子は、`next()`するたびに`arr[0]` `arr[1]` `arr[2]`…のように[^4]順番に要素へアクセスして、返ってきた値を`value`にセットしているようです。

空き枠だろうが何だろうがとにかくインデックス0から一つずつ参照して`value`に放り込むのです。

JavaScriptでは値が設定されていないプロパティ（≒空きスロット）を参照すると`undefined`になりますので、この`undefined`が`value`に入り、最終的に配列の要素になっていた…というのが真相のようです。

# 結論

スプレッド構文で配列の空き枠を`undefined`で埋められる理由は、**配列の反復子が`undefined`を返すから**で、その`undefined`は**値のないプロパティにアクセスした結果**でした。

~~そして、幾多の記事がこの点を解説していないのは反復子とかの説明が面倒だっただけなんじゃないか説が浮上しました。~~


[^1]: 元記事ではspread operator（スプレッド演算子）と書かれていますが、この`...`は仕様書では演算子とされていませんので、スプレッド構文と呼ぶことにします。
[^2]: 軽く調べただけなので、どこかにあるのかもしれません。
[^3]: 「共通の」というのはつまり、文字列でも配列でもその他のオブジェクトでも、このプロトコルに準拠してさえいればスプレッド構文やfor-of文などで同じように反復できるということです。
[^4]: 実際にはプロパティアクセサーではなく、`[[Get]]`内部メソッドというものが使われています。