29
24

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-10-02

事の発端

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

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

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

ほげー。

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

> 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の仕様書を探ってみました。

new Array(len)このあたりになります。読む限りではlengthを初期化する話はありますが、要素をどうこうするという記述はありません。

一方で要素で初期化する方を見てみると、

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を見てみると、

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なので、ループは回れど各要素には触れられない(関数も呼ばれない)ということが分かります。

納得です。

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

あたりを参照。

for文

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

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

Array.apply(null, Array(10))

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.

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

なるほど。

Array.prototype.fill

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

実はES6でArray.prototype.fillというのが追加されていたようです。

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

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

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

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

せやなぁ...

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

underscoreやlodashが使える場合

色々出来そうです。

_.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の場合。[ , , , , , , , , , ]。 

29
24
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
24