Перейти к содержанию

Массивы: порядок, изменение и копирование

Массив объединяет упорядоченные значения под одним именем. Мы уже обходили четыре названия и складывали количества, но пока использовали готовую запись без разбора её поведения. Теперь посмотрим, как получить конкретный элемент, изменить набор и сохранить исходную последовательность для повторного отображения.

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

Индекс и длина

Полностью замените app.js:

const titles = ['Современный JavaScript', 'HTML и CSS',
  'Производительность сайта', 'Статический сайт из Markdown'];
console.log(titles.length);
console.log(titles[0]);
console.log(titles[titles.length - 1]);

Ожидается число четыре, первое название и название Markdown-курса. Индекс первого элемента равен нулю; индекс последнего вычисляется как длина минус один. Это число обозначает позицию, а не постоянную идентичность курса. Если переставить карточки, позиции изменятся, хотя сами курсы останутся прежними.

Чтение отсутствующего индекса обычно даёт undefined. Это не сообщение «курс не опубликован», а отсутствие значения в выбранном месте массива. Не используйте позицию как долговременный идентификатор записи. В следующем уроке у каждой карточки появится отдельное свойство id, позволяющее узнавать её после перестановки.

length не является универсальным доказательством наличия корректных записей: JavaScript допускает разреженные массивы с пропущенными позициями. В нашем проекте создаём плотный набор из четырёх объектов и не присваиваем удалённые индексы. Позднее проверим форму записей этого плотного массива и массива из JSON. Такая проверка не будет универсальным валидатором разреженных массивов: некоторые методы обработки пропускают пустые позиции.

Изменение содержимого

К объявлению titles из первого примера можно временно добавить:

titles.push('Учебный дополнительный курс');
console.log(titles.length);
const removed = titles.pop();
console.log(removed);
console.log(titles.length);

Ожидаемая длина сначала пять, затем снова четыре. Метод push изменяет массив, добавляя значение в конец. pop удаляет последнее значение и возвращает его, поэтому мы можем посмотреть, что именно удалено. Для постоянных данных серии после эксперимента оставьте первоначальные четыре названия.

Объявление через const этому не препятствует: связь имени с объектом массива остаётся прежней, меняется его содержимое. Попытка titles = [] была бы переназначением и вызвала бы ошибку. Это уточняет обсуждение постоянных переменных: неизменность ссылки не означает запрет любых изменений объекта.

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

Ссылка и отдельный массив

Рассмотрим намеренно неудобный вариант:

const titles = ['Современный JavaScript', 'HTML и CSS'];
const visibleTitles = titles;
visibleTitles.pop();
console.log(titles.length);

Ожидается единица. Новое имя visibleTitles указывает на тот же массив, поэтому удаление видно через исходное имя. Присваивание не выполняет автоматическое копирование. Такая ошибка особенно неприятна, когда функция выглядит как подготовка отображения, но случайно редактирует общий источник.

Для отдельной последовательности используем slice без аргументов:

const titles = ['Современный JavaScript', 'HTML и CSS'];
const visibleTitles = titles.slice();
visibleTitles.pop();
console.log(titles.length);
console.log(visibleTitles.length);

Ожидаются два и один. Копия содержит те же строковые значения, но является другим массивом. Теперь удаление элемента копии не меняет количество исходных элементов. Общее поведение массивов и поверхностных копий приведено в MDN об Array.

Копирование через slice является поверхностным. Когда элементами станут объекты, копия массива сохранит ссылки на те же объекты. Изменение свойства курса через копию будет видно в исходных данных. Для нашего отображения это не проблема, потому что мы только читаем свойства и переставляем записи, но ограничение важно назвать до начала работы с объектами.

Получение части последовательности

Метод slice также позволяет взять диапазон. При titles.slice(0, 2) выбираются элементы с индексами ноль и один; конечная граница два не включается. Исходный массив сохраняется. Так можно подготовить две первые карточки для небольшого блока, не удаляя остальные из общей библиотеки.

Не путайте его с splice: похожее название скрывает другой результат и изменение исходного массива. Чтобы не запоминать API по звучанию, проверяйте два вопроса: какая величина возвращается и меняется ли источник. Эти вопросы пригодятся для sort, filter и map в следующих уроках.

Массив может содержать значения разных типов, но это не делает смешанный набор удобным. Список из названия, числа и функции не обозначает четыре курса и потребует помнить смысл каждого места. У всех элементов каталога будет одна согласованная структура объекта. Язык позволяет смешение, приложение выбирает более строгую модель ради понятности.

Для самостоятельного наблюдения создайте копию четырёх названий, удалите в копии последний элемент и выведите обе длины. Затем выведите последний элемент исходника. Ожидается, что исходная последовательность всё ещё включает Markdown-курс. Это проверяемая формулировка обязанности: подготовка видимого набора сохраняет источник.

Равные значения и идентичность набора

Два массива с одинаковыми строками остаются двумя разными объектами. При строгом сравнении самого массива с его копией результат будет ложным, хотя последовательности элементов совпадают. Поэтому вопрос «тот же источник?» отличается от вопроса «одинаковое содержимое?». Для карточек сначала будет важна именно сохранность источника, а сравнение содержимого потребовало бы отдельного правила.

Не используйте delete titles[1] как обычное удаление карточки. Оно оставит пропущенную позицию, а длина может сохраниться. Такой набор сложнее обходить и проверять. Если интерфейс действительно редактирует последовательность, выберите операцию с ясным результатом, а для отображения лучше создайте новый отобранный массив. В этой серии исходные данные вообще не удаляются после выбора темы.

При наблюдении объекта в консоли инструменты иногда показывают его раскрываемое состояние после изменения. Поэтому для простого опыта выводите конкретные примитивные значения: длину, первое название, последнее название. Эти сообщения яснее показывают момент чтения, чем раскрытие ссылки на массив спустя несколько действий. Это особенность диагностики, а не изменение семантики присваивания. Чтение длины возвращает число в момент выполнения строки; ссылка на сам объект позволяет инструменту исследовать его содержимое отдельно.

Методы массива могут возвращать не то, что кажется по названию. Например, push возвращает новую длину, а не сам добавленный курс и не весь обновлённый массив. Если присвоить этот результат переменной visibleTitles, она окажется числом. Тогда последующий обход не получит ожидаемую последовательность. Сначала читайте контракт метода, затем называйте сохранённое значение по его роли.

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

Пустой массив также является настоящим массивом, а не отсутствующим значением. Проверка Array.isArray([]) будет истинной. Это позволяет определить успешный каталог без записей отдельно от неизвестного источника. Проверка boolean самого массива не отвечает на вопрос пустоты: объект остаётся истинным независимо от количества элементов. Для количества смотрите length, сохраняя оговорку о корректности записей.

В следующем уроке заменим раздельные списки названий и количеств массивом объектов. У каждой записи будут собственные свойства, а постоянный id отделит идентичность карточки от её текущего номера на странице.

Оглавление курса