Как инициализировать длину массива в JavaScript? - PullRequest
431 голосов
/ 31 января 2011

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

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

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

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

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

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

Ответы [ 17 ]

443 голосов
/ 19 февраля 2015
  • 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]

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

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

    new Array(4);
    

    создает пустой массив длины 4. Но

    new Array('4');
    

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

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

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

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}
195 голосов
/ 01 марта 2017

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

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

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

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

56 голосов
/ 21 декабря 2017

Самый короткий:

[...Array(1000)]
40 голосов
/ 09 апреля 2018
[...Array(6)].map(x=>0);

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

ИЛИ

Array(6).fill(0);

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

Примечание: вы не можете зациклить пустые слоты Array(4).forEach( (_,i) => {} )

ИЛИ

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

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

( typcript safe )


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

При создании 2D массив с 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] ]

Инициализация шахматной доски

var Chessboard = [...Array( 8 )].map( (x,j) =>
    Array( 8 ).fill( null ).map( (y,i) =>
        `${ String.fromCharCode( 65 + i ) }${ 8 - j }`));

// [ [ A8,B8,C8,D8,E8,F8,G8,H8 ],
//   [ A7,B7,C7,D7,E7,F7,G7,H7 ],
//   [ A6,B6,C6,D6,E6,F6,G6,H6 ],
//   [ A5,B5,C5,D5,E5,F5,G5,H5 ],
//   [ A4,B4,C4,D4,E4,F4,G4,H4 ],
//   [ A3,B3,C3,D3,E3,F3,G3,H3 ],
//   [ A2,B2,C2,D2,E2,F2,G2,H2 ],
//   [ A1,B1,C1,D1,E1,F1,G1,H1 ] ]
33 голосов
/ 10 января 2018

ES6 представляет Array.from, который позволяет создавать Array из любых "массивоподобных" или итераций объектов:

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

В этом случае {length: 10} представляет минимальное определение «массива» объекта : пустой объект с только определенным свойством length.

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

25 голосов
/ 31 января 2011

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

var x = [,,,,];
16 голосов
/ 23 апреля 2014

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

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

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

8 голосов
/ 26 февраля 2014

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

Итак, после прочтения этого мне было любопытно, действительно ли предварительное распределение было быстрее, потому что в теории это должно быть. Тем не менее, этот блог дал несколько советов против этого 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
7 голосов
/ 02 марта 2016

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

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

Поскольку в данный момент в 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 постоянно резервирует столько же места, сколько и самый большой массив, который вы запрашивали.

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

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 на той же хромированной консоли или примерно в 2000 раз медленнее). Это будет не только распределение, но и 10000 нажатий для цикла и т. Д.

...