13

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?)

Community
  • 1
  • 1
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • 4
    FWIW, ECMA Harmony has [`String#repeat`](http://wiki.ecmascript.org/doku.php?id=harmony:string.prototype.repeat) which diminishes the usefulness of `new Array` somewhat. – Andy E Dec 09 '11 at 23:41

3 Answers3

3

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
RightSaidFred
  • 11,209
  • 35
  • 35
  • Or `Array.apply( null, arrayLike )` to convert array-like objects (like node-lists, or jQuery objects) to arrays. I tend to use `[].slice.call( arrayLike )` for this, but the first method seems "more proper"... **Live demo:** http://jsfiddle.net/W8DjF/ – Šime Vidas Dec 10 '11 at 00:19
  • I think the OP is talking about the Array constructor as in `new Array(n)`, not just the Array object. – jfriend00 Dec 10 '11 at 00:20
  • @ŠimeVidas: Yes, I just did some testing in IE6 to make sure it worked that far back. It does, but only on the same objects that `.slice()` works on, in other words Arrays and Arguments. No other type of Array-like objects. Thought maybe there was a shot that it would work. – RightSaidFred Dec 10 '11 at 00:28
  • @jfriend00: But they're equivalent calls. – RightSaidFred Dec 10 '11 at 00:29
  • 1
    The main thing in my experience with – Pointy Dec 10 '11 at 00:35
  • 4
    @Pointy: The suspense is killing me! – RightSaidFred Dec 10 '11 at 00:38
  • @ŠimeVidas: Oh wait, I just tried it in Chrome, and it doesn't work. Where were you testing? – RightSaidFred Dec 10 '11 at 00:46
  • 2
    Oh crap sorry about that @RightSaidFred - I got interrupted. Anyway the issue is that when you're using a server-side template language (php, jsp, whatever), and you code a `new Array(${foo})` where "foo" is *usually* a list of numbers, but sometimes it's just one number, then things don't work so well :-) I just like being in the habit of using the bracket notation; it's less typing anyway. – Pointy Dec 10 '11 at 00:50
  • @Pointy: Yeah, there are certainly situations where it wouldn't be wise to use it, and I do generally use `[]`. I'd just not be too quick to relegate it to a position of illegitimate. – RightSaidFred Dec 10 '11 at 00:57
  • @ŠimeVidas: Yep, Firefox works, but Chrome gives me an IE reminiscent `Uncaught TypeError: Function.prototype.apply: Arguments list has wrong type`. – RightSaidFred Dec 10 '11 at 00:58
  • @RightSaidFred I use Firefox. It also works in Opera and IE9. I don't have Safari on my laptop. Could it be that this is a Chrome-specific issue? – Šime Vidas Dec 10 '11 at 01:02
  • 1
    @ŠimeVidas: [Here's the issue](http://code.google.com/p/v8/issues/detail?id=917). – RightSaidFred Dec 10 '11 at 01:09
  • @ŠimeVidas: FWIW, you seem to [get a slight edge](http://jsperf.com/array-vs-slice) in Firefox with `Array.apply` – RightSaidFred Dec 10 '11 at 01:46
  • @RightSaidFred I suspected that the performance difference is because `[].slice` creates an empty array just to get the reference to `slice`, but even if `Array.prototype.slice` is used, and even if its cached, it's still slower `Array.apply`: http://jsperf.com/array-vs-slice/2 – Šime Vidas Dec 10 '11 at 01:53
  • Oh, and yes I just tested Safari, and it works, so it's V8 specific. – RightSaidFred Dec 10 '11 at 14:11
  • @ŠimeVidas: FYI, the [Chrome issue](http://code.google.com/p/v8/issues/detail?id=917) is now [fixed](http://code.google.com/p/v8/source/detail?r=9709). – RightSaidFred Dec 12 '11 at 13:56
  • @RightSaidFred Yay `:)` Btw, I'll get back to this thread later today. I have some updates to make to my original question... – Šime Vidas Dec 12 '11 at 16:02
  • "*How about as an alternate way to shallow clone an Array?*" - no. Try your code with `arr = [2]` to see it fail. You're right: `slice` is just simpler, and almost nobody knows what they're doing :-) – Bergi Oct 26 '17 at 22:55
3

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.

Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
3

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;
}
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • You can create your 3 dimensional Array with jQuery `$.map` if you just wrap each Array returned in another Array. It flattens the outer, but not the inner. – RightSaidFred Dec 10 '11 at 13:20
  • @RightSaidFred, is that a bug or a feature :D – Esailija Dec 10 '11 at 13:26
  • Great question. :) The real bug is that it flattens in the first place, which just doesn't make sense for a `map` function. Alas, [it won't be fixed](http://bugs.jquery.com/ticket/6183). It's an internal function that they felt comfortable with exposing in the public API irrespective of its unusual behavior. – RightSaidFred Dec 10 '11 at 13:37
  • @Raynos, not so fast. [].map doesn't work as explained later in the question :P `$.map( new Array(10), Number ) [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]` vs `new Array(10).map( Number ) []` – Esailija Dec 10 '11 at 13:51
  • @Esailija at least promote a homebrew `map` function – Raynos Dec 10 '11 at 13:52
  • @Raynos, I am using homebrew function in my last example :P I might as well change them all since I have to use homebrew map function – Esailija Dec 10 '11 at 13:54