I liked this question - Legitimate uses of the Function constructor - so I wanted to create a similar question regarding the Array
constructor.
Of course, the array literal notation is the correct way to create arrays. This would mean that the new Array
notation should not be used. And "case closed".
However, there is one specificity of the new Array
form. If a natural number is passed in, an empty array is created and its length
property is set to that number.
So
arr = new Array( 7 );
is equivalent to
arr = [];
arr.length = 7;
This can be considered a feature. I was wondering if this "feature" has real-world uses. I recently stumbled upon one such (simple) use:
new Array( n + 1 ).join( '*' ) // returns string containing n stars
// e.g.
new Array( 3 ).join( '*' ) // returns '**'
new Array( 6 ).join( '*' ) // returns '*****'
This is cool, but was hoping for some more advanced uses. (Something that would make the new Array
notation a legitimate tool in JavaScript programs.)
Update: I've noticed that the jQuery library uses the new Array( len )
notation in one instance - it's inside the when
function (search for "when:"
):
when: function( firstParam ) {
var args = sliceDeferred.call( arguments, 0 ),
i = 0,
length = args.length,
pValues = new Array( length ),
count = length,
pCount = length,
// etc.
They use it to initialize the pValues
local variable, which is used in a local function further down in the code:
function progressFunc( i ) {
return function( value ) {
pValues[ i ] = arguments.length > 1 ?
sliceDeferred.call( arguments, 0 ) : value;
deferred.notifyWith( promise, pValues );
};
}
I would love to know if changing the assignment to just
pValues = [],
would break the program... (Is new Array( length )
required for notifyWith
to work properly?)
I don't know if this counts, but one case where you'd use the constructor is if you need to be sure you have clean, unmodified Array constructor. You can create an "iframe sandbox"
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var Safe = frames[frames.length - 1];
And then create arrays like this...
var things = new Safe.Array('x', 'y', 'z');
...knowing that your arrays will not have any foreign stuff in the prototype put there by other scripts.
That's not making use of that single-parameter-as-array-length feature, though. The only thing that's probably really good for is setting up huge arrays to benchmark stuff.
Pretty much anything you can come up with a homebrewn map function (native .map
doesn't iterate and jQuery.map
is just full bugs)
Creating ranges:
//1-20
map( Array( 20 ), function( v,i ){
return i+1;
});
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
//-50 - 0
map( Array( 51 ), function( v,i ){
return i-50;
});
//[-50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
In general, instead of doing:
var a = [],
l = 10;
while ( l-- ) {
a.unshift(
(function(l){
return function() {
alert( l );
};
})( l )
);
}
You can do this:
var a = map( Array( 10 ), function ( v, i ) {
return function(){
alert( i );
};
});
Note that this only applies to jQuery.map
or shimmed .map
, native .map
doesn't iterate the array.
If you're not using jQuery( which flattens the result :( ) you can create
three dimensional arrays like this:
var xyz = map(Array(10), function (v, i) {
return map(Array(10), function (v, j) {
return map(Array(10), function (v, k) {
return i * 100 + j * 10 + k;
});
});
});
xyz[0][0][0] // 0
xyz[9][9][9] // 999
xyz[4][4][4] // 444
xyz[3][5][8] // 358
Of which the equivalent of for loops is pretty horrific :P
Quick implementation of a map function for completeness:
function map( elems, callback ){
var i, length = elems.length, ret = [];
for ( i = 0; i < length; i++ ) {
value = callback( elems[ i ], i );
ret[ ret.length ] = value;
}
return ret;
}
How about as an alternate way to shallow clone an Array?
var arr = [1,2,3];
var arr2 = Array.apply( null, arr );
arr === arr2; // false;
arr.length === arr2.length; // true
Yes, I'm reaching here because you'd just use .slice()
, but then I really don't see using Array
as illegitimate in the first place, as long as you know what you're doing.
Getting off topic, but one way to make a common function that utilizes Array
or slice
. Requires bind
. Too lazy right now to update for older browsers.
http://jsfiddle.net/fdgBU/1/
var slicer,
apply = Function.prototype.apply;
if( apply.bind ) {
try {
(slicer = apply.bind( Array, null ))({length:0});
} catch( e ) {
slicer = apply.bind([].slice);
}
} else {
slicer = function( coll ) {
if( !coll || coll.length !== +coll.length ) return;
var res = [], i = 0, len = coll.length >>> 0;
for( ; i < len; ++i ) {
res[i] = coll[i];
}
return res;
};
}
var arr_like = {'0':true,'1':true,'2':true,length:3},
arr = slicer( arr_like );
console.log( arr ); // [true, true, true]
console.log( arr.length === arr_like.length); // true