Массив 1…N в JavaScript: от простого к производительному
Привет, коллега-разработчик! Если ты когда-нибудь сталкивался с задачей «создать массив чисел от 1 до N» — ты не один. Это настолько популярный вопрос на Stack Overflow, что он собрал тысячи просмотров и десятки вариантов ответов. Казалось бы, что может быть проще? Но дьявол, как всегда, кроется в деталях: производительности, читаемости и универсальности кода.
Сегодня мы разберём все основные способы создания массива [1, 2, 3, ..., N] на JavaScript, сравним их по скорости, элегантности и области применения. Ты узнаешь, какой метод выбрать для обычного проекта, а какой — когда каждая миллисекунда на счету. Поехали!
Почему это важно? Контекст задачи
Представь: ты пишешь алгоритм сортировки, генерируешь тестовые данные, создаёшь сетку для игры или просто хочешь быстро получить последовательность чисел. Во всех этих случаях нужен массив от 1 до N. Казалось бы — цикл for и вперёд. Но современный JavaScript предлагает столько синтаксического сахара, что легко запутаться.
Мы рассмотрим 5 основных подходов, от классики до современных однострочников, и проверим их на прочность в реальных условиях. Готовь консоль браузера и любимый редактор — будет жарко!
Метод 1: Классический цикл for
Базовый, но надёжный способ, который знает каждый новичок:
function createArrayLoop(n) {
const arr = [];
for (let i = 1; i <= n; i++) {
arr.push(i);
}
return arr;
}
console.log(createArrayLoop(5)); // [1, 2, 3, 4, 5]
Плюсы:
- Максимальная читаемость — понятно даже тем, кто только начал учить JS.
- Нет скрытых вызовов или магии.
- Работает во всех средах, включая очень старые браузеры.
Минусы:
- Медленнее, чем некоторые альтернативы (но не критично для малых N).
- Много кода для простой задачи.
Метод 2: Array.from() с map-функцией
Элегантный современный подход, появившийся в ES6:
const createArrayFrom = n => Array.from({ length: n }, (_, i) => i + 1);
console.log(createArrayFrom(5)); // [1, 2, 3, 4, 5]
Здесь мы создаём массив с заданной длиной и сразу заполняем его через map-функцию. Второй аргумент Array.from — это как раз функция-маппер, которая получает текущий индекс i и возвращает i + 1.
Плюсы:
- Очень кратко и выразительно.
- Не требует предварительного создания пустого массива.
- Хорошая производительность.
Минусы:
- Может быть менее очевиден для новичков.
- Создаёт промежуточный объект
{ length: n }.
Метод 3: Spread-оператор + keys()
Один из самых популярных однострочников на Stack Overflow:
const createArraySpread = n => [...Array(n).keys()].map(i => i + 1);
console.log(createArraySpread(5)); // [1, 2, 3, 4, 5]
Как это работает? Array(n) создаёт разреженный массив из n пустых элементов. .keys() возвращает итератор индексов (0, 1, 2...). Spread-оператор разворачивает его в обычный массив, а затем map сдвигает значения на +1.
Плюсы:
- Очень лаконично (один вызов).
- Выглядит «магически» — приятно удивляет коллег на код-ревью.
Минусы:
- Самый медленный из всех методов для больших N (из-за двойного прохода и создания промежуточных массивов).
- Сложнее для понимания начинающими.
- Потребляет больше памяти.
Метод 4: Array.fill() + map()
Ещё один популярный вариант:
const createArrayFill = n => Array(n).fill().map((_, i) => i + 1);
console.log(createArrayFill(5)); // [1, 2, 3, 4, 5]
Сначала создаём массив из n пустых элементов, затем заполняем его значениями (fill() без аргументов ставит undefined), и только потом map превращает индексы в числа.
Плюсы:
- Проще для понимания, чем spread + keys.
- Быстрее, чем spread-версия.
Минусы:
- Всё ещё два прохода (fill + map).
- Немного медленнее
Array.from.
Метод 5: Uint8Array и typed arrays (для спецзадач)
Если тебе нужна максимальная производительность и ты работаешь с числами в диапазоне 0-255, можно использовать типизированные массивы:
const createTypedArray = n => Uint8Array.from({ length: n }, (_, i) => i + 1);
console.log(createTypedArray(5)); // Uint8Array [1, 2, 3, 4, 5]
Этот метод возвращает не обычный массив, а Uint8Array. Он занимает меньше памяти (1 байт на элемент вместо 8 для обычного числа) и быстрее обрабатывается.
Плюсы:
- Экономия памяти (до 8 раз).
- Очень высокая скорость для больших N.
Минусы:
- Ограничение по значениям (0-255 для Uint8).
- Несовместимость с обычными методами массивов без преобразования.
- Избыточно для большинства задач.
Бенчмарк: кто быстрее?
Давайте посмотрим на реальную производительность. Я прогнал тесты в Node.js для N = 10 000 и N = 100 000 (среднее по 100 запускам). Результаты (чем меньше — тем лучше):