Массив 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. Лучше избегать таких массивов вообще.