Массивы: основы

коллекции≈ 7 минут

Здесь будет встроен видео-разбор темы (5–7 минут, YouTube / RuTube / VK Видео) — тем, кто лучше воспринимает на слух, текст ниже не обязателен.

Объект удобен, когда у каждого значения есть имя. Но списку покупок имена не нужны — нужен порядок. Для упорядоченных данных в JavaScript есть массив: пронумерованный ряд ячеек, где важно, что идёт первым, а что третьим.

Разделы темы:·

Массив создают квадратными скобками: внутри перечисляют элементы через запятую. Пустой массив — просто [], в него потом можно что-нибудь положить.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Массив не требует, чтобы элементы были одного типа. Числа, строки, логические значения, другие массивы, функции — всё это спокойно лежит рядом. Вложенный массив достают двумя парами скобок подряд.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Есть второй способ создания — new Array(). В реальном коде его почти не встретишь, и вот почему: с одним числовым аргументом он создаёт не массив из одного числа, а массив заданной длины из пустых ячеек. Двусмысленность на ровном месте — пишите квадратные скобки.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Элементы нумеруются с нуля: первый — arr[0], второй — arr[1]. Поэтому последний элемент всегда arr[arr.length - 1]. Привыкайте к этой формуле, она встретится вам сотни раз; есть и короткая запись arr.at(-1), которая считает с конца.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Обращение к несуществующему индексу не роняет программу — возвращается undefined. Это удобно и одновременно опасно: опечатка в индексе не вызовет ошибку, а тихо передаст undefined дальше по коду. Запись по индексу работает так же просто: присвоили — заменили.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

length — не количество элементов, а наибольший индекс плюс один. Пока массив заполнен подряд, разницы не видно, и об этом легко забыть. Но стоит присвоить далёкий индекс — и длина прыгает через пустоту.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Второе, что важно знать: length доступно для записи. Уменьшили — массив обрезался, и вернуть отрезанное увеличением длины уже нельзя, на месте элементов будет пустота. Отсюда короткий приём очистки: arr.length = 0.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Четыре метода закрывают почти всю повседневную работу со списком. Два работают с концом массива: push добавляет, pop забирает. Два — с началом: unshift добавляет, shift забирает. Все четыре меняют исходный массив, а не возвращают копию.

МетодЧто делаетЧто возвращает
push(...)добавляет в конецновую длину
pop()удаляет последнийудалённый элемент
unshift(...)добавляет в началоновую длину
shift()удаляет первыйудалённый элемент

push и unshift принимают сколько угодно значений сразу: arr.push(1, 2, 3) добавит три элемента за один вызов. Комбинацией push + shift получается очередь: кто раньше встал, тот раньше вышел.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

push и pop трогают только последнюю ячейку. shift и unshift сдвигают весь остаток массива и перенумеровывают его, поэтому на списке из миллиона элементов разница в скорости уже заметна.

Под капотом массив — обычный объект с числовыми ключами и особым свойством length. Отсюда все странности: typeof для массива возвращает "object", и отличить массив от объекта приходится отдельной функцией Array.isArray.

Главное следствие важнее: переменная хранит не сам массив, а ссылку на него. Присвоили другой переменной — обе смотрят на один и тот же массив в памяти, и изменение через одну видно через другую. Это не баг, а то же поведение, что у объектов.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Раз массив — объект, ему можно добавить нечисловое свойство. Делать так не стоит: length его не заметит, перебор по индексам пропустит, а движок потеряет внутренние оптимизации, ради которых массивы и существуют.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Если присвоить далёкий индекс или удалить элемент через delete, в массиве появятся дыры — ячейки, которых физически нет. Внешне они выглядят как undefined, но это не одно и то же: часть методов дыры пропускает, а движок переводит такой массив в медленный режим хранения. Чтобы удалить элемент по-настоящему, используют splice, а не delete.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Классический способ — цикл по индексам: он даёт и номер, и значение. Если номер не нужен, короче и читаемее for..of — он идёт прямо по значениям. Третий вариант, forEach, вызывает вашу функцию для каждого элемента и передаёт в неё значение и индекс.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

А вот for..in для массивов не применяют. Он перебирает ключи объекта: индексы придут строками, в перебор попадут добавленные вручную свойства, а работает он заметно медленнее. Для массива это лишний риск без единой выгоды.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

  • Массив — упорядоченный список: элементы нумеруются с нуля, последний доступен как arr[arr.length - 1] или arr.at(-1).
  • length — это наибольший индекс плюс один, а не количество элементов; свойству можно присвоить значение, и массив обрежется без возможности вернуть удалённое.
  • push и pop работают с концом массива, unshift и shift — с началом; вторая пара сдвигает весь остаток и потому медленнее.
  • Массив — это объект: typeof возвращает "object", проверяют через Array.isArray, а переменная хранит ссылку, а не копию.
  • Перебирают массив циклом по индексам, for..of или forEach; for..in для массивов не используют, а дыр от delete и далёких индексов избегают.

1/3Массив создали как [1, 2, 3], затем выполнили arr[7] = 8. Чему теперь равно arr.length?

2/3Какая пара методов работает с началом массива?

3/3let a = [1, 2]; let b = a; b.push(3); console.log(a.length) — что выведется?

Сумма цен

Дан массив цен. Посчитайте их сумму и выведите её. Сделайте это двумя способами: через for..of и через цикл по индексам — результат должен совпасть.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

С for..of код читается почти как фраза: «для каждой цены из массива прибавь её к сумме».

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Через индексы получится то же самое, только длиннее — и здесь легко ошибиться в границе цикла:

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Условие именно i < prices.length, а не <=: последний индекс на единицу меньше длины. С <= на последнем шаге вы получите undefined, и сумма превратится в NaN.

Обрезать список без slice

В списке шесть дел, а показать нужно только первые три. Условие: не использовать slice и не создавать новый массив — задача решается одной строкой.

js
Tab добавляет отступ. Чтобы выйти из редактора, нажмите Escape — фокус перейдёт на кнопку запуска.

Свойству length можно присвоить значение, и всё, что оказалось за новой границей, отбрасывается:

jsлистинг · не запускается
Листинг только для чтения: этот код не запускается в браузере.

Это единственный способ укоротить массив «на месте». slice(0, 3) вернул бы новый массив, а исходный остался бы шестиэлементным. И помните про необратимость: обратное присваивание todo.length = 6 вернёт длину, но не дела — на их местах будут пустые ячейки.