Javascript "arrays" (those with Array
prototype, not typed arrays), are just objects, therefore this
var m = [];
m[268435461] = -1;
is exactly the same as
var m = {
"268435461": -1
}
except that in the first case, m
has the Array
prototype and a special length
property.
However, methods defined in Array.prototype
(like forEach
or join
) are trying to hide that fact and "emulate" sequential arrays, as they exist in other languages. When iterating their "this" array, these methods take its length
property, increase the loop counter from 0
upto length-1
and do something with the value under the key String(i)
(or undefined
if there's no such key)
// built-in js array iteration algorithm
for (let i = 0; i < this.length - 1; i++) {
if (this.hasOwnProperty(String(i))
do_something_with(this[String(i)])
else
do_something_with(undefined)
Now, length
of an array is not a number of elements in it, as the name might suggest, but rather the max numeric value of its keys + 1, so in your case, length
will be 268435462
(check it!)
When you do m < 0
, that is, compare a non-number to a number, JS converts them both to strings, and Array.toString
invokes Array.join
, which, in turn, uses the above loop to convert elements to strings and insert a comma in between:
// built-in js Array.join algorithm
target = '';
for (let i = 0; i < this.length - 1; i++) {
let element = this[String(i)]
if(element !== undefined)
target += element.toString()
target += ','
}
Illustration:
m = [];
m[50] = 1;
console.log(m.join())
This involves lots of memory allocations, and that's what is causing the delay.
(After some more testing, the allocation are not the deciding factor here, "hollow" loops will cause the same slowdown:
console.time('small-init')
var m = [];
m[1] = -1;
console.timeEnd('small-init')
console.time('small-loop')
m.forEach(x => null)
console.timeEnd('small-loop')
console.time('big-init')
var m = [];
m[1e8] = -1;
console.timeEnd('big-init')
console.time('big-loop')
m.forEach(x => null);
console.timeEnd('big-loop')
That being said, I don't think modern JS engines are that silly, and implement iterations exactly as described above. They do have array-specific optimizations in place, but these optimizations are targeted at "good" sequential arrays, and not at bizarre edge cases like this. Bottom line: don't do that!