はじめに
const arr = new Array(10).map((value, index) => { ... });
のようなコードを書いていてうまくmapが回らなく困ったので、調べていたら以下の記事を見つけた。
new Array(len)と[undefined, ...]の違い - Qiita
なるほど、ここから察するにnew Array(len)はlengthは初期化するけれど、各要素は初期化されない、というよりは確保されていないような動作であることが分かりました。
new Array(2)[1]のように要素アクセスするとundefinedを返しますが、これは要素が返されているのではなく、プロパティがないのでundefinedというわけですね。
なるほど、undefinedが定義されているわけではないので、mapで関数が呼ばれていないらしい。
ECMAScript Language Specification - ECMA-262 Edition 5.1
ES2015+では
上記記事にあるようにfill()
すれば問題なく、当初はそのやり方で対応していた。が、ES2015+なら以下の書き方でもいけるとの知見を得た。
const arr = [...Array(10)].map((value, index) => { ... });
[...Array(n)]は何をしている?
Spread Operatorで配列を展開している。
最初はmapと同じで要素が初期化されていないから展開もできないと思っていて、不思議だったので同様にECMAScriptの仕様を見てみる。
ECMAScript 2015 Language Specification – ECMA-262 6th Edition
ArgumentList : ... AssignmentExpression
1. Let list be an empty List.
2. Let spreadRef be the result of evaluating AssignmentExpression.
3. Let spreadObj be GetValue(spreadRef).
4. Let iterator be GetIterator(spreadObj).
5. ReturnIfAbrupt(iterator).
6. Repeat
a. Let next be IteratorStep(iterator).
b. ReturnIfAbrupt(next).
c. If next is false, return list.
d. Let nextArg be IteratorValue(next).
e. ReturnIfAbrupt(nextArg).
f. Append nextArg as the last element of list.
ざっくり見ると Array.prototype.map と違ってhasOwnProperty
のチェックはしていないっぽい。
なので初期化がされていない要素はそのままundefined
として返されており、結果undefined
でのfillと同様の動きをしているみたい。
仕様を読む大切さを知った。