Legitimate uses of the Array constructor

2020-05-23 08:34发布

问题:

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

回答1:

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.



回答2:

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;
}


回答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