Создание и заполнение массивов произвольной длины в JavaScript

JavaScript

Оригинальная ссылка:2 ali hat.com/2018/12/пока горячо…

Лучший способ создать массив — использовать литерал:

const arr = [0,0,0];

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

1. Массивы без пустых элементов работают лучше

В большинстве языков программирования массив представляет собой непрерывную последовательность значений. существуетJavaScriptсередина,Array— это словарь, отображающий индексы на элементы, он может иметь пустые элементы, а пустые элементы также имеют свои соответствующие индексы, но не сопоставляются с элементами. Например, в следующем массиве есть пустой элемент с индексом 1.

> Object.keys(['a',, 'c'])
[ '0', '2' ]

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

  • Словарь, поиск занимает больше времени и памяти.
  • Непрерывная структура данных со значениями тегов для пустых элементов. Проверка того, является ли значение пустым элементом, требует дополнительного времени. В любом случае, если движок встречает пустой элемент, он не может просто вернутьundefined, а на обход цепочки прототипов и поиск свойства с именем empty element index уходит больше времени.

В некоторых двигателях, напримерV8, переключение на менее производительную структуру данных является постоянным. Даже если все пустые элементы имеют добавленную стоимость, они не переключатся обратно.

СвязанныйV8Дополнительные сведения о представлении массивов см. в статье Матиаса Байненса.Типы элементов в V8.

2. Создайте массив

конструктор массива

Обычно конструктор массива используется для создания массива заданной длины.

const LEN = 3;
const arr = new Array(LEN);
assert.equal(arr.length, LEN);
// arr only has holes in it
assert.deepEqual(Object.keys(arr), []);

Этот способ удобен, но у него есть два недостатка:

  • Эти пустые элементы делают этот массив немного медленнее, даже если вы позже добавите значение с полным.
  • Пустой элемент редко является начальным «значением» элемента. Например,0чаще.

2.2 Конструктор массива плюс метод .fill()

.fill()метод изменяет существующий массив и заполняет его указанным значением. Это помогает в прохождении новогоArray()Инициализируйте массив после его создания:

const LEN = 3;
const arr = new Array(LEN).fill(0);
assert.deepEqual(arr,[0,0,0]);

предупреждать:Если ты.fill()Массив с объектами, все элементы которого ссылаются на один и тот же экземпляр (т.е. объект не клонирован):

const LEN = 3;
const obj = {};

const arr = new Array(LEN).fill(obj);
assert.deepEqual(arr, [{}, {}, {}]);

obj.prop = true;
assert.deepEqual(arr,
  [ {prop:true}, {prop:true}, {prop:true} ]);

Мы встретимся с методом заполнения, который не имеет этой проблемы позже (черезArray.from()).

2.3 Метод .push()

const LEN = 3;shu
const arr = [];
for (let i=0; i < LEN; i++) {
  arr.push(0);
}
assert.deepEqual(arr, [0, 0, 0]);

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

2.4 Заполнение неопределенных массивов

Array.from()Будуiterablesтип иlike-ArrayЗначение типа преобразуется в массив. Он обрабатывает пустые элементы как неопределенные элементы. Мы можем использовать его для преобразования каждого пустого элемента в undefined:

> Array.from({length: 3})
[ undefined, undefined, undefined ]

параметр{length:3}представляет собой массивоподобный объект длины 3, содержащий только пустые элементы. также можно использоватьnew Array(3), но обычно создает более крупные объекты.

Расширение массива работает только сiterableзначение, и сArray.from()имеет аналогичный эффект:

> [...new Array(3)]
[ undefined, undefined, undefined ] 

Фу,Array.from()пройти черезnew Array()В результате его создания вы все равно получите разреженный массив.

2.5 Отображение с помощью Array.from()

Если вы предоставляете функцию карты в качестве второго аргумента, вы можете использоватьArray.from()на карту.

заполнить массив значениями

  • Создайте массив небольших целых чисел:
> Array.from({length:3},()=> 0)
[0,0,0]
  • Создайте массив с уникальными (не общими) объектами:
> Array.from({length:3},()=>({}))
[{},{},{}]

Создать диапазон целочисленных значений

  • Создайте массив с возрастающими целыми числами:
> Array.from({length:3},(x,i)=> i)
[0,1,2]
  • Создайте произвольные целочисленные диапазоны:
> const START = 2,END = 5;
> Array.from({length:END-START},(x,i)=> i + START)
[2,3,4]
  • Другой способ создать массив с возрастающими целыми числами — это.keys(), который также рассматривает уязвимости как неопределенные элементы:
> [... new Array(3).keys()]
[0,1,2]

.keys()Возвращает итерируемый объект. Мы преобразуем его в массив с помощью оператора распространения.

3. Дополнение: создать массив

  • Заполнить пустые элементы или неопределенные:
new Array(3)
→ [ , , ,]
Array.from({length: 2})
→ [undefined, undefined]
[...new Array(2)]
→ [undefined, undefined]
  • Введите любое значение:
const a=[]; for (let i=0; i<3; i++) a.push(0);
→ [0, 0, 0]
new Array(3).fill(0)
→ [0, 0, 0]
Array.from({length: 3}, () => ({}))
→ [{}, {}, {}] (unique objects)
  • Целочисленный диапазон:
Array.from({length: 3}, (x, i) => i)
→ [0, 1, 2]
const START=2, END=5; Array.from({length: END-START}, (x, i) => i+START)
→ [2, 3, 4]
[...new Array(3).keys()]
→ [0, 1, 2]

3.1 Рекомендации по методу

Я предпочитаю следующий подход, основное внимание уделяется удобочитаемости, а не производительности.

  • Вам нужно создать пустой массив, который вы будете заполнять полностью, что дальше?
new Array(LEN)
  • Вам нужно создать массив, инициализированный примитивными значениями?
new Array(LEN).fill(0)
  • Вам нужно создать массив, инициализированный объектом?
Array.from({length: LEN}, () => ({}))
  • Вам нужно создать серию целых чисел?
Array.from({length: END-START}, (x, i) => i+START)

Если вы имеете дело с массивами целых чисел или чисел с плавающей запятой, рассмотрите возможность создания массива для этой цели.типизированный массив. Они не могут иметь пустых элементов и всегда инициализируются нулями.

намекать:Производительность массива обычно не так важна

  • В большинстве случаев я не буду слишком беспокоиться о проблемах с производительностью. Даже массив пустых элементов также очень быстр. Мы можем подумать, как сделать код более осмысленным.

  • Кроме того, то, как и где производится оптимизация движка, постоянно меняется. Завтра всегда будет быстрее, чем сегодня.

4. Благодарности

Спасибо Матиасу Байненсу и Бенедикту Мёреру за помощь в пониманииV8деталь.

5. Дальнейшее чтение