Массив Array — упорядоченный набор значений с числовой индексацией. Создаётся литералом [1, 2, 3] или конструктором new Array(3) (но последний почти никогда не нужен — он часто делает не то, что ожидаешь, см. подводные камни ниже).
Важная техническая деталь: с точки зрения движка массив — это объект с числовыми ключами и специальным свойством length. Из-за этого typeof [1, 2] возвращает "object", а не "array". Проверка «это массив или нет» делается через Array.isArray(x), не через typeof.
Когда массив надо превратить во что-то ещё
Несколько типичных кейсов из реального кода:
- Сериализация для URL. Чтобы положить список тегов в query-параметр, его надо превратить в строку: tags.join(',') → "css,js,vue", а на бэке распарсить обратно через split(',').
- Отображение в UI. Список авторов в виде "Сергей, Анна, Дима" — это .join(', ') внутри шаблона. Просто {names} в JSX или Vue использует toString(), который тоже зовёт join(','), но без пробелов.
- Поиск по ключу. Чтобы получать пользователя по ID быстро, массив users часто превращают в объект { [id]: user } через Object.fromEntries(users.map(u => [u.id, u])). Доступ O(1) против O(n) для поиска в массиве.
- Подсчёт. Длина массива в число — самое частое +arr.length или просто arr.length (она и так число). Реже — сумма элементов: arr.reduce((a, b) => a + b, 0).
- Проверка наличия. if (arr.length) — идиома «есть ли что-то в массиве». Прямой if (arr) работает не так, как ожидается: пустой массив — объект, объект — truthy.
Пять направлений преобразования
Из массива можно получить любой другой тип. Каждое направление разбирается отдельно — ниже короткий обзор.
- Массив в строку — arr.join('|') с произвольным разделителем, или arr.toString() со стандартной запятой. Для сложных структур — JSON.stringify(arr): вернёт "[1,2,3]" с сохранением вложенности.
- Массив в число — обычно через arr.length, реже через arr.reduce((a, b) => a + b, 0) для суммы. Number([42]) вернёт 42 (одноэлементный), но Number([1, 2]) — NaN.
- Массив в булевое — прямой Boolean(arr) всегда true (даже для []). Чтобы получить «true только когда есть элементы», нужно arr.length > 0 или Boolean(arr.length).
- Массив в объект — через Object.fromEntries(arr.map(item => [item.id, item])) для индексации. Или { ...arr } — даст объект с числовыми ключами { 0: ..., 1: ... }.
- Массив в специальное значение — пустой [] в null или undefined через тернарник arr.length ? arr : null.
Как проверить, что перед нами массив
В JavaScript массив технически — объект, поэтому typeof для него не годится. Используется специальный статический метод:
typeof [1, 2, 3] // "object" — не помогает
typeof "hello" // "string" — а тут помогает
typeof { a: 1 } // "object" — то же самое, что массив
Array.isArray([1, 2, 3]) // true
Array.isArray("hello") // false
Array.isArray({ length: 3 }) // false (просто объект со свойством length)
Array.isArray(arguments) // false (псевдо-массив, не настоящий)
В старом коде до ES5 встречались проверки через Object.prototype.toString.call(x) === '[object Array]' — работающая, но монструозная конструкция. Сейчас везде Array.isArray().
Псевдо-массивы и как из них сделать настоящие
В JS встречаются объекты, которые выглядят как массивы (есть числовые индексы и length), но не имеют массивных методов. Главные подозреваемые: arguments внутри обычных функций, NodeList из querySelectorAll, HTMLCollection от document.getElementsByClassName, итераторы. Чтобы превратить их в настоящий массив:
// Способ 1: spread (только для итерируемых объектов)
const nodes = [...document.querySelectorAll('.item')];
// Способ 2: Array.from (универсальный, даже для не-итерируемых псевдо-массивов)
const args = Array.from(arguments);
// Array.from ещё и вторым аргументом принимает map-функцию:
const ids = Array.from(nodes, node => node.dataset.id);
// Способ 3 (старый, не рекомендуется): Array.prototype.slice.call(...)
const old = Array.prototype.slice.call(arguments);
В современном коде — [...x] или Array.from(x). Первый короче; второй универсальнее (работает с не-итерируемыми объектами вроде { length: 3, 0: 'a', 1: 'b', 2: 'c' }).
Подводные камни
new Array(3) и [3] — разные вещи. new Array(3) создаёт «разреженный» массив длины 3 без значений (holes). [3] — массив из одного элемента 3. Это рождает странности: new Array(3).map(x => 0) вернёт всё тот же разреженный массив, потому что .map пропускает holes. Чтобы получить [0, 0, 0], нужен Array.from({ length: 3 }, () => 0) или Array(3).fill(0).
Сложение массивов через +. [1, 2] + [3, 4] возвращает не [1, 2, 3, 4], а строку "1,23,4". Это потому, что + для массивов запускает их преобразование в строку (через join(',')), а потом конкатенирует. Чтобы объединить массивы, используй arr1.concat(arr2) или [...arr1, ...arr2].
Сравнение через == и ===. [1, 2] === [1, 2] — false. Массивы сравниваются по ссылке, не по содержимому. Для глубокого сравнения нужен либо JSON.stringify(a) === JSON.stringify(b) (быстрый, но не работает на циклических структурах), либо библиотечный deepEqual из lodash или подобной.
Boolean([]) — true, а [] == false — тоже true. Один из самых известных багов языка. В одном выражении то же значение даёт разные результаты. Объяснение: Boolean([]) работает строго (объект всегда truthy), а == запускает каскад каст-операций: массив → строка "" → число 0, и сравнение 0 == false даёт true. Поэтому — всегда ===.
Holes — пропуски в массиве. [1, , 3] — массив с пропуском в индексе 1. Большинство методов (map, filter, forEach) holes пропускают, но for...of, indexOf, includes — видят их как undefined. Лучше избегать таких массивов вообще.