Как инициализировать длину массива в javascript?

большинство учебников, которые я читал на массивах в JavaScript (включая w3schools и devguru) предположим, что вы можете инициализировать массив с определенной длиной, передав целое число конструктору массива с помощью var test = new Array(4); синтаксис.

после использования этого синтаксиса в моих файлах js я запустил один из файлов через jsLint, и он взбесился:

ошибка: Ошибка в строке 1 символ 22: ожидается ") "и вместо этого увидел "4".
var test = новый массив (4);
Проблема в строке 1 символ 23: ожидаемый '; ' и вместо этого увидел ')'.
var test = новый массив (4);
Проблема в строке 1 символ 23: ожидал идентификатор и вместо этого увидел ')'.

после прочтения объяснение jsLint своего поведения, похоже, jsLint не очень нравится new Array() синтаксис, а вместо этого предпочитает [] при объявлении матрицы.

так у меня есть пара вопросов. Во-первых, почему? Я рискую, используя синтаксис? Есть ли несовместимости браузера, о которых я должен знать? И во-вторых, если я переключусь на синтаксис квадратных скобок, есть ли способ объявить массив и установить его длину на одной строке, или мне нужно сделать что-то вроде этого:

var test = [];
test.length = 4;

16 ответов


  1. почему вы хотите инициализировать длину? Теоретически в этом нет необходимости. Это даже может привести к путанице, потому что все тесты, которые используют length чтобы узнать, является ли массив пустым или нет, сообщит, что массив не пуст.
    некоторые тесты показать, что установка начальной длины больших массивов можете быть более эффективным, если массив заполняется после этого, но увеличение производительности (если есть) кажется отличаются от браузера к браузеру.

  2. jsLint не нравится new Array() потому что конструктор является неоднозначным.

    new Array(4);
    

    создает пустой массив длиной 4. Но!--16-->

    new Array('4');
    

    создает массив содержит значение '4'.

Что касается вашего комментария: в JS вам не нужно инициализировать длину массива. Она растет динамично. Вы можете просто сохранить длину в некоторых переменная, например,

var data = [];
var length = 5; // user defined length

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}

  • Array(5) дает вам массив с длиной 5, но без значений, поэтому вы не можете перебирать его.

  • Array.apply(null, Array(5)).map(function () {}) дает вам массив с длиной 5 и неопределенными значениями, теперь его можно перебирать.

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) дает вам массив с длиной 5 и значения 0,1,2,3,4.

  • Array(5).forEach(alert) ничего не делает, Array.apply(null, Array(5)).forEach(alert) дает вам 5 предупреждений

  • ES6 дает нам Array.from теперь вы также можете использовать Array.from(Array(5)).forEach(alert)

  • если вы хотите инициализировать с определенным значением, это хорошо знает...
    Array.from('abcde'), Array.from('x'.repeat(5))
    или Array.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]


С ES2015 .fill() теперь вы можете просто сделать:

//`n` is the size you want to initialize your array
//`value` is what the array will be filled with
Array(n).fill(value)

что намного более лаконично, чем Array.apply(0, new Array(n)).map(i => value)

можно опустить value и звонок .fill без аргумента, который заполнит массив с undefined. (однако это не удастся в Typescript).


минимальные:

[...Array(1000)]

это инициализирует свойство length до 4:

var x = [,,,,];

ЕС6 вводит Array.from что позволяет создать Array любой "array-like" или iterables объекты:

Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

в этом случае {length: 10} - минимальное определение "array-like" объект: пустой объект только с length свойства определены.

Array.from позволяет второй аргумент для сопоставления по результирующему массиву.


Я удивлен, что не было предложено функциональное решение, которое позволяет установить длину в одной строке. Следующее основано на UnderscoreJS:

var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);

по причинам, упомянутым выше, я бы избегал этого, если бы не хотел инициализировать массив до определенного значения. Интересно отметить, что существуют другие библиотеки, реализующие диапазон, включая Lo-dash и Lazy, которые могут иметь разные характеристики производительности.


[...Array(6)].map(x=>0);

// [0, 0, 0, 0, 0, 0]

или

Array(6).fill(0);

// [0, 0, 0, 0, 0, 0]

или

Array(6).fill(null).map( (x,i) => i );

// [0, 1, 2, 3, 4, 5]

( машинопись безопасный )


создание вложенных массивов

при создании массив fill интуитивно следует создавать новые экземпляры. Но то, что на самом деле произойдет, - это тот же массив, который будет храниться как ссылка.

var a = Array(3).fill([6]); 
// [[6], [6], [6]]

a[ 0 ].push( 9 );
// [[6,9], [6,9], [6,9]]

решение

var a = [...Array(3)].map(x=>[]); 

a[ 0 ].push( 4, 2 );
// [[4,2], [ ], [ ]]

таким образом, массив 3x2 будет выглядеть примерно так:

[...Array(3)].map(x=>Array(2).fill(0));

// [ [0,0] ,
//   [0,0] ,
//   [0,0] ]

(это был, наверное, лучше в качестве комментария, но слишком долго)

Итак, после прочтения этого мне было любопытно, действительно ли предварительное выделение было быстрее, потому что теоретически это должно быть. Тем не менее, этот блог дал несколько советов, советующих против этогоhttp://www.html5rocks.com/en/tutorials/speed/v8/.

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

var time = Date.now();
var temp = [];
for(var i=0;i<100000;i++){
    temp[i]=i;
}
console.log(Date.now()-time);


var time = Date.now();
var temp2 = new Array(100000);
for(var i=0;i<100000;i++){
    temp2[i] = i;
}
console.log(Date.now()-time); 

этот код дает следующее Через несколько случайные работает:

$ node main.js 
9
16
$ node main.js 
8
14
$ node main.js 
7
20
$ node main.js 
9
14
$ node main.js 
9
19

var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6

вот еще одно решение

var arr = Array.apply( null, { length: 4 } );
arr;  // [undefined, undefined, undefined, undefined] (in Chrome)
arr.length; // 4

первый аргумент apply() является привязкой этого объекта, о которой мы здесь не заботимся, поэтому мы устанавливаем его в null.

Array.apply(..) называет


пожалуйста, люди пока не отказываются от своих старых привычек. Существует большая разница в сложности между выделением памяти один раз, а затем работа с записями в этом массиве (как и раньше), и выделение его много раз по мере роста массива (что неизбежно, что система делает под капотом с другими предлагаемыми методами).

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

видя, как там еще кажется нет опции в JS на данный момент, чтобы установить начальную емкость массива, я использую следующее...

var newArrayWithSize = function(size) {
  this.standard = this.standard||[];
  for (var add = size-this.standard.length; add>0; add--) {
   this.standard.push(undefined);// or whatever
  }
  return this.standard.slice(0,size);
}

есть компромиссы:

  • этот метод занимает столько же времени, сколько и другие для первого вызова функции, но очень мало времени для последующих вызовов (если не запрашивать больший массив).
  • на standard array постоянно резервирует столько места, сколько самый большой массив, который вы просили.

но если это соответствует тому, что вы делаете там может быть выигрыш. Неофициальное время ставит

for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}

на довольно быстром (около 50 мс для 10000 учитывая, что с n=1000000 это заняло около 5 секунд), и

for (var n=10000;n>0;n--) {
  var b = [];for (var add=10000;add>0;add--) {
    b.push(undefined);
  }
}

в течение минуты (около 90 секунд для 10000 на той же консоли chrome или примерно в 2000 раз медленнее). Это будет не только распределение, но и 10000 толчков, для цикла и т. д..


конструктор массива имеет неоднозначный синтаксис, и JSLint просто ранит ваши чувства в конце концов.

кроме того, ваш пример кода-это сломанный, второй var заявление поднимет SyntaxError. Вы устанавливаете свойство length массива test, Так что нет необходимости для другой var.

насколько ваши варианты идут,array.length является единственным "чистым". Вопрос в том, почему вам нужно установить размер в первую очередь? Попробуйте преобразовать код в избавиться от этой зависимости.


Почему вы не должны использовать new Array демонстрируется этим кодом:

var Array = function () {};

var x = new Array(4);

alert(x.length);  // undefined...

какой-то другой код мог бы беспорядок с переменной массива. Я знаю, что это немного надуманно, что кто-то напишет такой код, но все же...

кроме того, как сказал Феликс Кинг, интерфейс немного непоследователен и может привести к некоторым очень трудным для отслеживания ошибкам.

если вам нужен массив с длиной = x, заполненный неопределенным (как new Array(x) будет делать), вы мог бы это сделать:

var x = 4;
var myArray = [];
myArray[x - 1] = undefined;

alert(myArray.length); // 4

как описано выше, используя new Array(size) это несколько опасно. Вместо того, чтобы использовать его напрямую, поместите его внутри "функции создателя массива". Вы можете легко убедиться, что эта функция ошибок, и вы избегаете опасности вызывать new Array(size) напрямую. Кроме того, вы можете дать ему необязательное начальное значение по умолчанию. Это createArray функция делает именно это:

function createArray(size, defaultVal) {
    var arr = new Array(size);
    if (arguments.length == 2) {
        // optional default value
        for (int i = 0; i < size; ++i) {
            arr[i] = defaultVal;
        }
    }
    return arr;
}

Вы можете задать длину массива с помощью array.length = youValue

Так было бы

var myArray = [];
myArray.length = yourValue;