Firebug represents (new Array(N))
as an array with N
undefined
s in it. I recently ran across a scenario that demonstrated that a sized array with all undefined
values in it is different from a newly constructed, sized array. I'd like to understand the difference.
Suppose you want to generate a list of random integers between 0 and 1000.
function kilorange() {
return Math.floor(Math.random() * (1001));
}
no_random_numbers = (new Array(6)).map(kilorange);
my_random_numbers = [undefined, undefined, undefined,
undefined, undefined, undefined].map(kilorange);
I would have expected no_random_numbers
and my_random_numbers
to be equivalent, but they're not. no_random_numbers
is another array of undefined
s, whereas my_random_numbers
is an array with six random integers in it. Furthermore, after throwing a console.count
statement into kilorange
, I learned that my function never gets called for the array created with the Array constructor.
What is the difference, and why does map
(and presumably other iterable methods) not treat the above arrays the same?
Firebug represents (new Array(N))
as an array with N
undefined
s in it. I recently ran across a scenario that demonstrated that a sized array with all undefined
values in it is different from a newly constructed, sized array. I'd like to understand the difference.
Suppose you want to generate a list of random integers between 0 and 1000.
function kilorange() {
return Math.floor(Math.random() * (1001));
}
no_random_numbers = (new Array(6)).map(kilorange);
my_random_numbers = [undefined, undefined, undefined,
undefined, undefined, undefined].map(kilorange);
I would have expected no_random_numbers
and my_random_numbers
to be equivalent, but they're not. no_random_numbers
is another array of undefined
s, whereas my_random_numbers
is an array with six random integers in it. Furthermore, after throwing a console.count
statement into kilorange
, I learned that my function never gets called for the array created with the Array constructor.
What is the difference, and why does map
(and presumably other iterable methods) not treat the above arrays the same?
6 Answers
Reset to default 7The ES standard (15.4.4.19) defines the algorithm for map
, and it's quite clear from step 8b that since your array doesn't actually have any of those elements, it will return an "empty" array with length 6.
As others have mentioned, it has to do with array objects in js being (unlike their rigid C counterparts) very dynamic, and potentially sparse (see 15.4 for the sparsity test algorithm).
When you use:
var a = new Array(N);
no values are stored in the new array and even the index "properties" are not created. That is why map
won't do a thing on that array.
The fact that Firebug does that is a bug/feature of the Firebug. You should remember that it's console is an eval
envelope. There are other bug/features in the Firebug console.
For example in Chrome console you'll see the above a
array as []
.
Look at this sample and run it: http://jsfiddle.net/ArtPD/1/ (it create the two arrays without using the map
over them and then list the key/values of each one of them)
I would say that (new Array(6))
doesn't allocate "named" properties (so it doesn't create "1": undefined, "2": undefined...) while the other form [undefined, ... ]
does.
In fact if I use a for (var i in ... )
the two outputs are:
no_random_numbers
my_random_numbers
0 undefined
1 undefined
2 undefined
3 undefined
4 undefined
5 undefined
Good question, good answers. I fiddled a bit with the map
prototype from MDN. If it's adapted like this, map
would work for a new Array([length])
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
var O = Object(this);
var len = O.length >>> 0;
if ({}.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
if (thisArg) {
T = thisArg;
}
A = new Array(len);
k = 0;
while(k < len) {
var kValue, mappedValue;
if (k in O || (O.length && !O[k])) {
// ^ added this
kValue = O[ k ];
mappedValue = callback.call(T, kValue, k, O);
A[ k ] = mappedValue;
}
k++;
}
return A;
};
Based on Casy Hopes answer you could also make a mapx
-extension to be able to use map
with some new Array([length])
:
Array.prototype.mapx = function(callback){
return this.join(',').split(',').map(callback);
}
//usage
var no_random_numbers = new Array(6).mapx(kilorange);
To answer the title question (why presize arrays), the only use that I've come across for initializing an Array(n)
array is to create an n-1
length string:
var x = Array(6).join('-'); // "-----"
To answer why you would presize an array, you'd do it so you don't have to do as many individual allocations. Allocating memory takes some time, and it might trigger a garbage collection run that can take a lot longer.
On a typical web page, you won't notice a difference, but if you're doing something major, it might help a fair bit.
new Array(6).join("some text")
which will repeat "some text" 6 times. – Tetaxa Commented Oct 16, 2011 at 7:50length
of the array does not have to be increased every time set a new element, though the performance gain is probably negligible. – Felix Kling Commented Oct 16, 2011 at 8:13