Массив — это упорядоченный список значений
Массивы в JavaScript — это упорядоченные последовательности значений. Внутри может лежать что угодно: числа, строки, объекты и даже другие массивы, причём типы элементов могут быть разными. Создать массив проще всего через квадратные скобки:
По сути, литеральный синтаксис — это то, чем вы будете создавать массивы в 99% случаев. Есть ещё вариант через new Array(...), но с ним легко порезаться: new Array(3) вернёт массив длиной 3 без единого реального элемента, а вовсе не [3]. Так что лучше придерживайтесь [].
Индексация начинается с нуля
К элементам массива обращаются по индексу, причём отсчёт идёт с 0:
Здесь стоит обратить внимание на пару моментов. Во-первых, обращение к индексу за пределами массива не бросает ошибку — вы просто получите undefined. Это классический источник багов в духе «почему тут undefined?». Во-вторых, вы можете присвоить значение по любому индексу, в том числе за последним элементом, — JavaScript спокойно расширит массив:
Позиции 3 и 4 превращаются в «пустые слоты» (разрежённые дыры). Длина при этом подскакивает до 6. На практике такое почти никогда не нужно — это побочный эффект гибкости массивов, а не фича, на которую стоит опираться.
Свойство length живое
array.length — это всегда на единицу больше максимального индекса. Оно обновляется автоматически, когда вы добавляете или удаляете элементы:
Последний приём стоит запомнить: присваивание значения свойству length обрезает массив. nums.length = 0 — быстрый способ очистить массив, не создавая новый.
push, pop, shift, unshift
Четыре метода для добавления и удаления элементов с концов массива:
push(x)добавляет элемент в конец и возвращает новую длину.pop()удаляет последний элемент и возвращает его.unshift(x)добавляет элемент в начало.shift()удаляет первый элемент.
push и pop работают быстро. А вот shift и unshift вынуждены переиндексировать все остальные элементы, поэтому на больших массивах они тормозят — для повседневных размеров это не проблема, но если у вас миллионы элементов, стоит иметь это в виду.
slice и splice в JavaScript: в чём разница
Названия у этих методов похожи, а поведение — абсолютно разное. Разберитесь с ними один раз, и сэкономите себе кучу времени и нервов.
slice(start, end) возвращает копию части массива. Исходный массив остаётся нетронутым. Параметр end не включается в результат:
splice(start, deleteCount, ...items) изменяет сам массив. С его помощью можно удалять, вставлять элементы или делать и то, и другое одновременно. Возвращает он то, что было удалено:
Правило простое: если слово ассоциируется с тем, как вы отрезаете кусок от буханки, — это slice. А если представляете, как склеивают киноплёнку (что-то вырезаем и вставляем), — это splice.
Поиск в массиве: indexOf, includes, find
Три способа найти нужное — каждый под свою задачу:
indexOf(x)возвращает индекс элемента или-1, если ничего не нашлось. Пригодится, когда нужна именно позиция.includes(x)возвращает булево значение. Удобнее, когда вам достаточно ответа «есть такой элемент или нет?». Плюс он корректно работает сNaN, в отличие отindexOf.find(predicate)проходит по элементам и возвращает первый, для которого функция-предикат вернулаtrue. Это основной вариант для массивов объектов —indexOfиincludesумеют сравнивать только по строгому равенству.
Перебор массива в JavaScript
Способов несколько, и каждый хорош в своей ситуации:
for...of — вариант по умолчанию: читается легко, работает с любым итерируемым объектом и сразу отдаёт значение. forEach подойдёт, если нужен ещё и индекс, и вы точно не собираетесь прерывать перебор на полпути (из forEach нельзя выйти через break). Классический for со счётчиком — многословный, зато даёт полный контроль: пригодится, когда надо пропускать элементы, идти с конца массива или и то и другое сразу.
А вот чего стоит избегать — это for...in. Он перебирает ключи и цепляет унаследованные свойства в придачу. Это инструмент для объектов, а не для массивов.
Копирование и объединение массивов
Массивы — это ссылочные значения. Присвоение одного массива другому не создаёт копию, а просто даёт одному и тому же массиву два имени:
Чтобы сделать настоящую копию, используйте spread-оператор или метод slice():
Оба способа дают поверхностную копию — сам массив получается новый, но если внутри лежат объекты, они по-прежнему общие. Ещё spread — это самый аккуратный способ склеить несколько массивов:
Массивы объектов
В реальном коде массивы чаще всего хранят именно объекты. Все те же методы работают и здесь, только условия поиска становятся куда интереснее:
filter возвращает новый массив с элементами, которые удовлетворяют условию. map возвращает новый массив, где каждый элемент преобразован. Их связка — хлеб с маслом при работе с массивами объектов, а на следующей странице мы подробнее разберём это семейство методов массивов JavaScript.
Массивы и объекты: когда что использовать
Короткая подсказка для интуиции: массив стоит брать, когда важен порядок и вы работаете со списком однотипных вещей. А объект (или Map) — когда нужно хранить данные по ключу-имени. «Первый, второй, третий» — это про массивы. «Найти пользователя с id 42» — это про объект или Map.
Смешивать, конечно, можно — массивы объектов встречаются на каждом шагу. Но не стоит использовать массив как словарь с ключами, а объект — как упорядоченный список. Каждый хорош в своей роли.
Что дальше: деструктуризация
Доставать значения из массивов по индексу (и свойства из объектов по имени) приходится так часто, что в JavaScript для этого есть отдельный синтаксис. Это и есть деструктуризация — как раз о ней поговорим дальше.
Часто задаваемые вопросы
Как создать массив в JavaScript?
Самый нормальный способ — литерал в квадратных скобках: const fruits = ['apple', 'banana']. Конструктор Array (new Array(3)) тоже существует, но на практике почти не нужен — new Array(3) создаёт массив длины 3 вообще без элементов, и это часто сбивает с толку.
Как удалить элемент из массива в JavaScript?
С конца — pop(), с начала — shift(). Чтобы выкинуть элемент из середины, используйте splice(index, 1) (меняет исходный массив) или filter(...) — он вернёт новый массив без нужного элемента. Главное помнить: splice мутирует, filter — нет.
В чём разница между slice и splice?
slice(start, end) возвращает поверхностную копию куска массива и оригинал не трогает. splice(start, deleteCount, ...items) меняет исходный массив: удаляет элементы, при желании вставляет новые, а возвращает те, что удалил. Одна буква — а поведение диаметрально разное.
Как проверить, есть ли значение в массиве?
Проще всего array.includes(value) — возвращает true или false и корректно работает с NaN. indexOf(value) вернёт индекс или -1, если элемента нет — удобно, когда нужна ещё и позиция. Для более сложных условий берите some(predicate) или find(predicate).