24

According to the MDN documentation for new Array(length) I can initialize an array with a given length as such:

var a = new Array(10);
a.length; // => 10
a; // => [undefined x 10]

However, apparently I can't use methods such as map(...) on the new array, even though arrays constructed in other ways work fine:

a.map(function() { return Math.random(); });
// => [undefined x 10] -- wtf?
[undefined, undefined].map(function() { return Math.random(); });
// => [0.07192076672799885, 0.8052175589837134]

Why is this the case?

I understand from this experience (and searching the web) that the array constructor with length is a black hole of unexplained behavior, but does the ECMA 262 specification offer an explanation?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
maerics
  • 151,642
  • 46
  • 269
  • 291
  • 3
    I'm guessing that `[undefined x 10]` is just a representation in your console that says that you have an empty 10-sized array. In Firefox, it just says `Array [ <10 empty slots> ]`. – Kevin Ji Feb 06 '15 at 17:35
  • 2
    FWIW `[undefined, undefined].map(function() { return Math.random(); });` doesn't do any operations on the undefined elements which is why it returns those floating point numbers. [If you passed them as a parameter into the callback and tried to use it, `map` would fail.](http://jsfiddle.net/bdwm4714/). [The `map` spec says](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) "it is not invoked for indexes that are undefined, those which have been deleted or which have never been assigned values." – Andy Feb 06 '15 at 17:40
  • @Andy: I dunno, this snippet behaves as expected --> `[undefined].map(function(x){return "OK:"+x;}) // => ["OK:undefined"]`. Perhaps that is the difference between "undefined values" and "[indexes] which have never been assigned values. – maerics Feb 06 '15 at 17:46
  • 1
    `undefined` is a value, a `hole` can been interpreted as `undefined` but it has no value. I think the problem is more to do with human language than the language of Javascript itself. `[,undefined][1]` has the value of `undefined` assigned to the property, `[,undefined][0]` is not defined it's a `hole` and has no value assigned to the property, you can interpret this as `undefined` but it's actually not defined. ECMA5 methods actually check that the property has a value assigned to it and ignores holes. Hope that makes sense. :) – Xotic750 Feb 06 '15 at 18:12
  • @Xotic750: ya, I think the issue is that the JS language overloads the term "length" to mean either "element count" (in most cases) or "capacity" (in the context of this question). For example, [Java `ArrayLists`](http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#ArrayList(int)) can be initialized with a positive *capacity* but that has no effect on *length* (size in java). Apparently JS doesn't make such a careful distinction. – maerics Feb 06 '15 at 18:30
  • `undefined` != *nothing at all*. Test in Firefox: `var a = []; a[10]=1; console.log(a)` prints `Array [ undefined, <9 empty slots>, 1 more… ]`. These empty slots are not iterable at all, and it might help to express this array as a object: `{10:1}` now, tell me if index 0 exists? Nope, it doesn't – This company is turning evil. Feb 06 '15 at 21:42

1 Answers1

19

new Array(10) doesn't return an array filled with 10 undefineds. Instead, it returns an array with no elements, and with a length property of 10.

See the difference:

var arr1 = new Array(1),
    arr2 = [undefined];
arr1.length === arr2.length; // Both are `1`
arr1[0] === arr2[1];         // Both are `undefined`
arr1.hasOwnProperty(0);      // false
arr2.hasOwnProperty(0);      // true

Therefore, ECMAScript 5 array methods skip those non-existing properties. Specifically, when they iterate from 0 to length, they check the [[HasProperty]] internal method of the array.

You can fix it easily with ECMAScript 6 Array.prototype.fill (which can be polyfilled):

new Array(10).fill().map(Math.random);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • 2
    Was gonna say the same, but you beat me to it. – Travis Feb 06 '15 at 17:35
  • 1
    Nice but whats the point of instantiate an array with length of 10 w/o elements? There is a useful case for it? – DontVoteMeDown Feb 06 '15 at 17:40
  • 3
    @DontVoteMeDown If you are going to build a huge array and tell the browser how many elements it will have, the browser can reserve the space in memory. Increasing the size at the insertion of each element may be slower. – Oriol Feb 06 '15 at 17:42
  • 2
    Ah, so it seems like the language is confusing "length" and "capacity" (despite the fact that a subsequent `push(...)` will insert at index "length+1" (wtf)? Seems like this is just a poorly thought out part of the language. – maerics Feb 06 '15 at 17:47
  • Don't think so: capacity is the maximum amount that something can contain, whereas length is the distance determined by the extent of something specified. It's inherent in the design of sparse arrays, it can be done so why not allow it? While not so commonly useful, I can think of ways it can be used. – Xotic750 Feb 06 '15 at 18:00
  • At least right now, it looks like [pre-allocation is faster](http://jsperf.com/pre-allocate-vs-resize/2) – Sean Vieira Feb 06 '15 at 18:15
  • @SeanVieira only for small arrays. http://jsperf.com/pre-allocate-vs-resize/3 – Dagg Nabbit Feb 06 '15 at 19:09
  • @Dagg - for newer Chrome / Opera it is still faster and Firefox 35 handles both about the same speed right now. It's a micro-optimization and as such is very brittle - knowing your target audience and continually testing your assumptions is the only way to go, if you *need* the performance bonus that worrying about such things gets you. – Sean Vieira Feb 06 '15 at 19:38
  • @SeanVieira see what happens when you bump it up to 1m items, though. At some point I think pre-allocating is going to be significantly slower. But I agree, it's not a significant enough optimization even for small arrays to legitimize the use of Array ctor. – Dagg Nabbit Feb 06 '15 at 20:25