Ruby arrays have a nice feature: you can construct a new array with an integer N, and a block, which will be called N times, to fill up the array:

1 Array.new(5) { 'yo' }
2 # gives:
3 ["yo", "yo", "yo", "yo", "yo"]
4 
5 # Closures, too!
6 i = 0
7 Array.new(4) { i = i + 1 }
8 # gives:
9 [1, 2, 3, 4]

I tried to recreate this in JavaScript:

1 new Array(5, function() { return "drip"; });
2 // gives:
3 [5, function() {
4     return "drip";
5 }]

Oops! I guess the Array constructor works differently in JavaScript. No worries, we can just call map on the new array.

1 new Array(5).map(function() { return "drip"; });
2 // gives:
3 [, , , , ]

…um, what? Shouldn’t that be ["drip", "drip", "drip", "drip", "drip"]? If I call new Array(3), I should get a brand new array, with 3 slots, all set to undefined; and I should be able to map over it, and fill up the array.

Let’s see what its elements are:

1 var array = new Array(5);
2 array[0]; // undefined, as expected
3 array[1]; // also undefined

So far, so good. What arguments are passed to the function?

1 function printAndDrip(arg) {
2     print(arg);
3     return "drip";
4 }
5 array.map(printAndDrip); // prints nothing, and returns [, , , , ]

It looks like the printAndDrip function is never being called, almost like the array has no contents.

Let’s try setting a value manually, then mapping:

1 array[2] = "hey there"; // [, , "hey there", , ], as expected
2 array.map(printAndDrip);
3 // prints "hey there", and returns [, , "drip", , ]

So, it only calls the function for values we’ve manually put there. Maybe map doesn’t call the function if the value of a slot is undefined? I know, I’m reaching here…

 1 array = [1, undefined, 2];
 2 array.map(printAndDrip);
 3 
 4 /* prints:
 5 1
 6 undefined
 7 2
 8 then outputs:
 9 ["drip", "drip", "drip"]
10 */

So it does call the function for undefined values! Then why didn’t it in our newly-created array?

This is when it hit me, and it’s a funny JavaScript fact that I always forget: JavaScript has fake arrays.

They’re actually closer to hash tables, whose keys are numbers. ["zero", "one"] is just syntax sugar: it creates an object with two properties, named 0 and 1; 0 points to “zero”, and 1 points to “one”.

1 // pretty much the same:
2 var arrayLiteral = ["zero", "one"];
3 var objectLiteral = { 0: "zero", 1: "one" };

Apparently, if you use the new Array(10) constructor, it creates an array with length 10, but with no named properties.

We can see the properties an object has with the hasOwnProperty method, so we can use that to test our hypothesis.

1 var emptyArray = new Array(10);
2 emptyArray.hasOwnProperty(0); // false
3 emptyArray.hasOwnProperty(1); // false
4 
5 var fullArray = [1,2,3];
6 fullArray.hasOwnProperty(0); // true
7 fullArray.hasOwnProperty(1); // true
8 fullArray.hasOwnProperty(99); // false: gone past the end

So where does that leave us? Nowhere, really. At least I’m a little clearer about JavaScript’s fake arrays. Imitating Ruby’s Array constructor is pretty much out; it’s easy enough, though a bit unsatisfying, to hand-roll our own:

 1 Array.filled = function(n, fn) {
 2     var array = [];
 3     while(n-- > 0) {
 4         array.push(fn());
 5     }
 6     return array;
 7 }
 8 Array.filled(5, function() { return "drip"; });
 9 // gives:
10 ["drip", "drip", "drip", "drip", "drip"]

Perhaps the folks working on the new JavaScript standards can put in a line-item about initializing Arrays with all the right numbered slots, and that’ll be unnecessary.

While writing this post, I used the JavaScript Shell 1.4 in FireFox 3.6.3 on Windows 7. I also redefined Array.prototype.toString to display JavaScript arrays the way you type them.