Массив - это упорядоченный список значений
Массивы в 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).