Законное использование конструктора Array

Мне понравился этот вопрос - Легитимное использование конструктора Function - так что я хотел создать аналогичный вопрос относительно конструктора Array.

Конечно, литеральная запись массива - это правильный способ создания массивов. Это означало бы, что обозначение new Array не должно использоваться. И "случай закрыт".

Однако существует одна специфичность формы new Array. Если передано натуральное число, создается пустой массив и его свойство length установлено на это число.

Итак,

arr = new Array( 7 );

эквивалентно

arr = [];
arr.length = 7;

Это можно рассматривать как функцию. Мне было интересно, имеет ли эта "функция" реальное использование. Я недавно наткнулся на одно такое (простое) использование:

new Array( n + 1 ).join( '*' ) // returns string containing n stars

// e.g.
new Array( 3 ).join( '*' ) // returns '**'
new Array( 6 ).join( '*' ) // returns '*****'

Это круто, но надеялся на некоторые более продвинутые цели. (Что-то, что сделало бы обозначение new Array законным инструментом в программах JavaScript.)


Обновление: Я заметил, что библиотека jQuery использует нотацию new Array( len ) в одном экземпляре - внутри функции when (поиск "when:"):

when: function( firstParam ) {
    var args = sliceDeferred.call( arguments, 0 ),
        i = 0,
        length = args.length,
        pValues = new Array( length ),
        count = length,
        pCount = length,
        // etc.

Они используют его для инициализации локальной переменной pValues, которая используется в локальной функции далее в коде:

function progressFunc( i ) {
    return function( value ) {
        pValues[ i ] = arguments.length > 1 ?
                sliceDeferred.call( arguments, 0 ) : value;
        deferred.notifyWith( promise, pValues );
    };
}

Я хотел бы знать, если изменить назначение только на

pValues = [],

будет разорвать программу... (Требуется ли new Array( length ) для notifyWith работать правильно?)

Ответы

Ответ 1

Как о альтернативном способе для мелкого клонирования массива?

var arr = [1,2,3];

var arr2 = Array.apply( null, arr );

arr === arr2; // false;

arr.length === arr2.length; // true

Да, я добираюсь сюда, потому что вы просто используете .slice(), но тогда я действительно не считаю использование Array незаконным в первую очередь, если вы знаете, что делаете.


Выход из темы, но один из способов сделать общую функцию, которая использует Array или slice. Требуется bind. Слишком ленив прямо сейчас для обновления для старых браузеров.

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

Ответ 2

Я не знаю, учитывается ли это, но в одном случае, когда вы используете конструктор, вам нужно убедиться, что у вас есть чистый, немодифицированный конструктор Array. Вы можете создать "изолированную песочницу iframe"

var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var Safe = frames[frames.length - 1];

И затем создайте такие массивы...

var things = new Safe.Array('x', 'y', 'z');

... зная, что ваши массивы не будут иметь каких-либо чужих материалов в прототипе, помещенных там другими скриптами.

Это не использование этой функции с одним параметром, как массив. Единственное, что, вероятно, действительно полезно для создания огромных массивов для тестирования.

Ответ 3

Довольно многое, что вы можете придумать с помощью функции отображения homebrewn (native .map не выполняет итерацию, а jQuery.map - только полные ошибки)

Создание диапазонов:

//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]

В общем, вместо выполнения:

var a = [],
    l = 10;

    while ( l-- ) {
        a.unshift( 
            (function(l){
            return function() {
                alert( l );
            };
            })( l )
        );
    }

Вы можете сделать это:

var a = map( Array( 10 ), function ( v, i ) {
    return function(){
        alert( i );
    };
});

Обратите внимание, что это относится только к jQuery.map или shimmed .map, native .map не выполняет итерацию массива.

Если вы не используете jQuery (который выравнивает результат:(), вы можете создать трехмерные массивы:

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

Из которых эквивалент для циклов довольно ужасен: P

Быстрая реализация функции отображения для полноты:

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