new Array(len)と[undefined, ...]の違い

# 事の発端

長さnで値は0/1ランダムな配列を作ろうとして、以下のようなコードを書きました。

```js
new Array(10).map(function() {
  return Math.floor(Math.random() * 2);
});
```

結果は`[1, 0, ...]`となるのが期待されますが、残念ながら`[undefined × 10]`[^1]になります。

ほげー。

# `new Array(1).hasOwnProperty(0)`が`false`を返す

```js
> new Array(1).length
1
> new Array(1).hasOwnProperty(0)
false
> [undefined].length
1
> [undefined].hasOwnProperty(0)
true
```

なるほど、ここから察するに`new Array(len)`はlengthは初期化するけれど、各要素は初期化されない、というよりは確保されていないような動作であることが分かりました。

`new Array(2)[1]`のように要素アクセスすると`undefined`を返しますが、これは要素が返されているのではなく、プロパティがないので`undefined`というわけですね。

# ECMA Scriptの仕様より

ググってもあまり情報が出てこないので[ECMA Scriptの仕様書](http://www.ecma-international.org/ecma-262/5.1/)を探ってみました。

`new Array(len)`は[このあたり](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.2.2)になります。読む限りでは`length`を初期化する話はありますが、要素をどうこうするという記述はありません。

一方で[要素で初期化する方](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.2.1)を見てみると、

> The 0 property of the newly constructed object is set to item0 (if supplied); the 1 property of the newly constructed object is set to item1 (if supplied); (以下略)

と書かれています。こちらに明示的に要素を初期化する記述があることを考えれば、そういったことが書かれていない`new Array(len)`は要素は初期化しないと考えるのが正しそうです。

続いて[`Array.prototype.map`](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.19)を見てみると、

> 6\. Let A be a new array created as if by the expression new Array(len) where Array is the standard built-in constructor with that name and len is the value of len.
> 7\. Let k be 0.
> 8\. Repeat, while k < len
> 　a. Let Pk be ToString(k).
> 　b. Let kPresent be the result of calling the [[HasProperty]] internal method of O with argument Pk.
> 　c. If kPresent is true, then
> 　...
> 9\. Return A.

と記述されています。`[[HasProperty]]`は`hasOwnProperty`と考えれば良さそうで、そうすると`new Array(len).hasOwnProperty(Pk)`は`false`なので、ループは回れど各要素には触れられない（関数も呼ばれない）ということが分かります。

納得です。

# 発端のプログラムをシンプルに書くには？

* [Create an array with same element repeated multiple times in javascript](http://stackoverflow.com/q/12503146)
* [Most efficient way to create a zero filled JavaScript array?](http://stackoverflow.com/q/1295584)

あたりを参照。

## for文

```js
var a = [];
for (var i = 0; i < 10; i++) {
  a.push(Math.floor(Math.random() * 2));
}
```

そりゃそうなのですが、そもそもforを書きたくないのが発端なので...。

## `Array.apply(null, Array(10))`

```js
Array.apply(null, Array(10)).map(...);
```

`new`は省略できるという話があるようです。

> 15.4.1 The Array Constructor Called as a Function
>
> When Array is called as a function rather than as a constructor, it creates and initialises a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.

http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.1

`Array.apply(null, Array(10))`は要するに`Array(undefined, undefined, ...)`なので、これなら`undefined`で初期化された10個の要素をもつ配列が生成されるというわけですね。

なるほど。

## `Array.prototype.fill`

```js
Array(10).fill(0).map(...);
```

実はES6で[`Array.prototype.fill`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill)というのが追加されていたようです。

Chrome 45には入っているようで、確かに動作しました。手元にあるio.js v2.3.4ではダメでした。

使えるならこれが一番良さそうですね。

## `Array(10).join(0).split('')`

```js
Array(10).join(1).split('').map(...)
```

せやなぁ...

シンプル明快ですが、文字列にしてsplitしているので効率はよろしくなさそうですね。

## underscoreやlodashが使える場合

色々出来そうです。

```js
_.range(10).map(function() { return _.random(1) });
_.range(1, 11, 0).map(_.random);
_.times(10, _.constant(1)).map(_.random);
_.fill(Array(10), 1).map(_.random);
```

便利ですね。


[^1]: ChromeのDevToolの場合。Nodeの場合。`[ , , , , , , , , ,  ]`。