何かおかしいところあったら教えてください。
ことの発端
テスト用のデータとして、['test1', 'test2', 'test3']
のように、100要素くらいある配列をサクッと生成してみたかった。
そして最終的に行き着いたのが次のコードだった。
[...(Array(10))].map((_, i) => `test${i}`);
その後調べてみると、より正しそうな方法を書いたブログを見つけたので、実際に使うときはここを参考にすると良さそう。
JavaScriptで0からn-1までを要素にもつArrayを作成する方法
さて、上記のブログで目的は達成されたが、何故自分が書いたコードが動いていたのだろう。色々と試してみた。
動作確認環境:Chrome 55.0
JavaScriptの配列の不思議
これは割と知っている人も多いことだと思うけど、Array(10)
と[10]
は違う。前者は「サイズが10の配列を作成」で、後者は「10という要素を持つ配列を作成」だ。
これを利用して、「10回ループを回すだけ」の目的で配列を作ろうとした。そのときのコードが以下。
Array(10).forEach(e => console.log('hoge'));
ただし、これは期待通りに動かない。原因は、Array()
がlengthプロパティは持つが、配列の各要素は持たない配列を生成するためだ。
よくわからないことを言っている気がするけど、次のサンプルを見ると明確だ。
Array(10).length;
// => 10
Array(10).hasOwnProperty(0);
// => false
[10].hasOwnProperty(0);
// => true
ここからわかる事は、Arrayのmap
・forEach
などの関数は、lengthプロパティだけでなくhasOwnProperty(又はそれに類するもの)を元にループを回すか判定しているということだ。
そんなこんなでArray(10).forEach()
というコードは動かないことがわかった。
そこで、(これは完全に思いつきで)ES2016のSpread Operatorを使ってみたらどうだろうと、試してみた。Spread Operatorは、オブジェクトや配列を展開してくれる式だ。
例えば、次のように使える。
const aray1 = [1, 2, 3];
const array2 = [...array1, 4, 5];
console.log(array2);
// => [1, 2, 3, 4, 5]
今回のケースだと、こういう形になる。
[...(Array(10))].forEach(e => console.log('hoge'));
すると、これは期待通りに動き、hoge
が10回出力される結果となった。Spread Operatorにより、lengthのみだった配列が、要素を持った通常の配列に変換された。
Array(10)
// => [undefined × 10]
[...(Array(10))]
// => [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
つまりSpread Operatorは、配列が実際に要素を持っているかどうかによらず、lengthの情報から配列の展開を行っているらしい。Array()
で初期化した配列は要素を持たないので、undefined
を10個持つ配列に変換されたようだ。
ちなみにArray.from()
でもSpreadOperatorと同様の結果になった。
Array.from(Array(10))
// => [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
結論
map
やforEach
はhasOwnProperty
相当の評価を行ってループを回しているが、Spread Operatorではその判定は無い。
さて、これが何の役に立つのか...