السلاسل النصية والعمل مع النص في JavaScript (Node.js): دليل شامل
Массив — это список. Один из самых используемых инструментов в программировании. Список пользователей, список товаров, список задач — всё это массивы. В этом уроке вы не только узнаете, как создавать массивы и работать с ними, но и освоите мощнейшие методы map, filter и reduce, которые делают код элегантным и лаконичным.
Что такое массив? Аналогия со списком
Представьте список покупок в блокноте: молоко, хлеб, яблоки, сыр, макароны. Каждый элемент находится на определённой строчке (первый, второй, третий...). Вы можете добавить новый пункт в конец, вычеркнуть что-то, проверить что уже куплено.
Массив в JavaScript — это exactly такой список, только в памяти компьютера. Формально: массив — это упорядоченная коллекция элементов, доступных по числовому индексу.
Зачем нужны массивы? Без них пришлось бы создавать отдельную переменную для каждого элемента:
// БЕЗ массива — ужасно:
let товар1 = "молоко";
let товар2 = "хлеб";
let товар3 = "яблоки";
// А если товаров 100? А если мы не знаем заранее сколько их?
// С массивом — отлично:
let покупки = ["молоко", "хлеб", "яблоки", "сыр", "макароны"];
Создание массива
// Литеральная запись (рекомендуется):
let пустойМассив = [];
let числа = [1, 2, 3, 4, 5];
let имена = ["Аня", "Боря", "Вася"];
let смешанный = [1, "два", true, null]; // Можно мешать типы (но не рекомендуется)
// Через конструктор (редко используется):
let тоже = new Array(1, 2, 3);
// Массив массивов (двумерный):
let матрица = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(матрица[1][2]); // 6 (вторая строка, третий элемент)
Индексы: почему с нуля?
Каждый элемент массива имеет номер — индекс. И здесь всех новичков ждёт сюрприз: индексы начинаются с 0, а не с 1!
Почему? Это идёт из математики и низкоуровневого программирования. Индекс показывает "смещение от начала". Первый элемент находится на нулевом смещении от начала массива. Технически это оптимальнее. Сейчас это просто исторически сложившееся соглашение.
let фрукты = ["яблоко", "банан", "апельсин", "груша"];
// Индексы: 0 1 2 3
console.log(фрукты[0]); // "яблоко" (первый)
console.log(фрукты[1]); // "банан" (второй)
console.log(фрукты[2]); // "апельсин" (третий)
console.log(фрукты[3]); // "груша" (четвёртый)
console.log(фрукты[4]); // undefined (такого элемента нет)
// Последний элемент:
let последний = фрукты[фрукты.length - 1]; // "груша"
// фрукты.length = 4, значит последний индекс = 4 - 1 = 3
// Изменение элемента:
фрукты[1] = "манго";
console.log(фрукты); // ["яблоко", "манго", "апельсин", "груша"]
Свойство length
let числа = [10, 20, 30, 40, 50];
console.log(числа.length); // 5 (количество элементов)
// Длина меняется при добавлении/удалении:
числа.push(60);
console.log(числа.length); // 6
// Можно принудительно обрезать массив:
числа.length = 3;
console.log(числа); // [10, 20, 30] — остальные удалены!
Основные методы: добавление и удаление
let стек = ["а", "б", "в"];
// push — добавить В КОНЕЦ (один или несколько):
стек.push("г");
console.log(стек); // ["а", "б", "в", "г"]
стек.push("д", "е");
console.log(стек); // ["а", "б", "в", "г", "д", "е"]
// pop — удалить последний элемент И ВЕРНУТЬ его:
let последний = стек.pop();
console.log(последний); // "е"
console.log(стек); // ["а", "б", "в", "г", "д"]
// unshift — добавить В НАЧАЛО:
стек.unshift("0");
console.log(стек); // ["0", "а", "б", "в", "г", "д"]
// shift — удалить первый элемент И ВЕРНУТЬ его:
let первый = стек.shift();
console.log(первый); // "0"
console.log(стек); // ["а", "б", "в", "г", "д"]
splice — универсальный метод редактирования
let цвета = ["красный", "зелёный", "синий", "жёлтый", "белый"];
// splice(индекс, количество_удалить, ...добавить):
// Удалить 2 элемента начиная с индекса 1:
let удалённые = цвета.splice(1, 2);
console.log(удалённые); // ["зелёный", "синий"]
console.log(цвета); // ["красный", "жёлтый", "белый"]
// Добавить элемент на позицию 1 (не удаляя ничего):
цвета.splice(1, 0, "фиолетовый");
console.log(цвета); // ["красный", "фиолетовый", "жёлтый", "белый"]
// Заменить элемент:
цвета.splice(2, 1, "оранжевый");
console.log(цвета); // ["красный", "фиолетовый", "оранжевый", "белый"]
Методы поиска
let числа = [3, 7, 1, 9, 2, 7, 4];
// indexOf — найти первый индекс элемента (-1 если нет):
console.log(числа.indexOf(7)); // 1 (первое вхождение)
console.log(числа.indexOf(99)); // -1 (не найдено)
// lastIndexOf — найти последний индекс:
console.log(числа.lastIndexOf(7)); // 5 (последнее вхождение)
// includes — проверить, есть ли элемент:
console.log(числа.includes(9)); // true
console.log(числа.includes(99)); // false
// find — найти первый элемент, подходящий по условию:
let первоеБольшое = числа.find(n => n > 5);
console.log(первоеБольшое); // 7
// findIndex — найти индекс первого подходящего:
let индекс = числа.findIndex(n => n > 5);
console.log(индекс); // 1
Метод forEach: перебор элементов
forEach — это как сказать: "для каждого элемента в списке — сделай вот это". Замена цикла for для работы с массивами.
let имена = ["Аня", "Боря", "Вася", "Галя"];
// Традиционный цикл for:
for (let i = 0; i < имена.length; i++) {
console.log(`Привет, ${имена[i]}!`);
}
// forEach — элегантнее:
имена.forEach(function(имя) {
console.log(`Привет, ${имя}!`);
});
// Со стрелочной функцией ещё короче:
имена.forEach(имя => console.log(`Привет, ${имя}!`));
// forEach также даёт индекс и сам массив:
имена.forEach((имя, индекс) => {
console.log(`${индекс + 1}. ${имя}`);
});
// 1. Аня
// 2. Боря
// 3. Вася
// 4. Галя
Важно: forEach ничего не возвращает (return undefined). Он нужен только для "побочных эффектов" — вывода, изменения внешних переменных, и т.д.
Метод map: трансформация массива
map — один из самых полезных методов. Он создаёт НОВЫЙ массив, применяя функцию к каждому элементу. Аналогия: как конвейер на фабрике — каждый элемент проходит через "преобразователь" и получается новый элемент.
let числа = [1, 2, 3, 4, 5];
// Создать новый массив, удвоив каждый элемент:
let удвоенные = числа.map(n => n * 2);
console.log(удвоенные); // [2, 4, 6, 8, 10]
console.log(числа); // [1, 2, 3, 4, 5] — исходный не изменился!
// Квадрат каждого числа:
let квадраты = числа.map(n => n ** 2);
console.log(квадраты); // [1, 4, 9, 16, 25]
// Массив объектов → массив строк:
let пользователи = [
{ имя: "Аня", возраст: 20 },
{ имя: "Боря", возраст: 25 },
{ имя: "Вася", возраст: 18 }
];
let имена = пользователи.map(п => п.имя);
console.log(имена); // ["Аня", "Боря", "Вася"]
let карточки = пользователи.map(п => `${п.имя} (${п.возраст} лет)`);
console.log(карточки); // ["Аня (20 лет)", "Боря (25 лет)", "Вася (18 лет)"]
Метод filter: фильтрация массива
filter создаёт новый массив, оставляя только те элементы, для которых функция-условие вернула true. Как фильтр для воды — что-то пропускает, что-то задерживает.
let числа = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Только чётные числа:
let чётные = числа.filter(n => n % 2 === 0);
console.log(чётные); // [2, 4, 6, 8, 10]
// Только числа больше 5:
let большие = числа.filter(n => n > 5);
console.log(большие); // [6, 7, 8, 9, 10]
// Реальный пример: фильтр товаров:
let товары = [
{ название: "Ноутбук", цена: 50000, наличие: true },
{ название: "Мышь", цена: 1500, наличие: false },
{ название: "Клавиатура", цена: 3000, наличие: true },
{ название: "Монитор", цена: 25000, наличие: true },
{ название: "Наушники", цена: 5000, наличие: false }
];
// Только товары в наличии:
let вНаличии = товары.filter(т => т.наличие);
console.log(вНаличии.map(т => т.название)); // ["Ноутбук", "Клавиатура", "Монитор"]
// Товары дешевле 10000 в наличии:
let доступные = товары.filter(т => т.наличие && т.цена < 10000);
console.log(доступные.map(т => т.название)); // ["Клавиатура"]
Метод reduce: свёртка массива
reduce — самый мощный и самый сложный для понимания метод. Он "сворачивает" массив до одного значения, накапливая результат. Аналогия: как подводить итог по списку — суммировать все расходы за месяц.
// Синтаксис: массив.reduce((аккумулятор, текущий) => ..., начальноеЗначение)
let числа = [1, 2, 3, 4, 5];
// Сумма всех чисел:
let сумма = числа.reduce((накоп, текущий) => накоп + текущий, 0);
console.log(сумма); // 15
// Как это работает:
// Шаг 1: накоп=0, текущий=1 → 0+1=1
// Шаг 2: накоп=1, текущий=2 → 1+2=3
// Шаг 3: накоп=3, текущий=3 → 3+3=6
// Шаг 4: накоп=6, текущий=4 → 6+4=10
// Шаг 5: накоп=10, текущий=5 → 10+5=15
// Максимальное число:
let максимум = числа.reduce((макс, текущий) => текущий > макс ? текущий : макс, -Infinity);
console.log(максимум); // 5
// Реальный пример: сумма заказа:
let корзина = [
{ товар: "Кофе", цена: 350, количество: 2 },
{ товар: "Круассан", цена: 120, количество: 3 },
{ товар: "Сок", цена: 180, количество: 1 }
];
let итого = корзина.reduce((сумма, позиция) => {
return сумма + позиция.цена * позиция.количество;
}, 0);
console.log(`Итого к оплате: ${итого} руб.`); // Итого к оплате: 1420 руб.
Цепочки методов
Одна из главных суперсил массивов — методы можно цепочкой, один за другим:
let студенты = [
{ имя: "Аня", оценка: 85, группа: "А" },
{ имя: "Боря", оценка: 60, группа: "Б" },
{ имя: "Вася", оценка: 92, группа: "А" },
{ имя: "Галя", оценка: 75, группа: "А" },
{ имя: "Дима", оценка: 55, группа: "Б" }
];
// Найти среднюю оценку студентов группы "А":
let средняяА = студенты
.filter(с => с.группа === "А") // оставить только группу А
.map(с => с.оценка) // взять только оценки
.reduce((сумма, оц) => сумма + оц, 0) / студенты.filter(с => с.группа === "А").length;
console.log(`Средняя оценка группы А: ${средняяА}`); // 84
Spread оператор (...)
Spread оператор (...) "разворачивает" массив — как высыпать содержимое коробки на стол.
let массив1 = [1, 2, 3];
let массив2 = [4, 5, 6];
// Объединить массивы:
let объединённый = [...массив1, ...массив2];
console.log(объединённый); // [1, 2, 3, 4, 5, 6]
// Добавить элементы в середину:
let с99 = [...массив1, 99, ...массив2];
console.log(с99); // [1, 2, 3, 99, 4, 5, 6]
// Скопировать массив (не ссылку!):
let копия = [...массив1];
копия.push(100);
console.log(массив1); // [1, 2, 3] — оригинал не изменился
console.log(копия); // [1, 2, 3, 100]
// Передать элементы массива как аргументы функции:
let числа = [3, 1, 4, 1, 5, 9, 2, 6];
let макс = Math.max(...числа); // То же что Math.max(3, 1, 4, 1, 5, 9, 2, 6)
console.log(макс); // 9
Другие полезные методы
let числа = [3, 1, 4, 1, 5, 9, 2, 6];
// sort — сортировка (ВАЖНО: по умолчанию как строки!):
let строковаяСорт = [10, 9, 2, 1, 21].sort();
console.log(строковаяСорт); // [1, 10, 2, 21, 9] — неправильно!
// Правильная числовая сортировка:
let правильная = [10, 9, 2, 1, 21].sort((а, б) => а - б);
console.log(правильная); // [1, 2, 9, 10, 21] — правильно!
// reverse — перевернуть:
console.log([1, 2, 3].reverse()); // [3, 2, 1]
// join — объединить в строку:
console.log(["Привет", "мир"].join(" ")); // "Привет мир"
console.log([1, 2, 3].join("-")); // "1-2-3"
// slice — вырезать подмассив (не меняет оригинал):
let буквы = ["а", "б", "в", "г", "д"];
console.log(буквы.slice(1, 3)); // ["б", "в"] (с 1 до 3, не включая 3)
console.log(буквы.slice(-2)); // ["г", "д"] (последние 2)
// flat — распрямить вложенные массивы:
let вложенный = [1, [2, 3], [4, [5, 6]]];
console.log(вложенный.flat()); // [1, 2, 3, 4, [5, 6]] (1 уровень)
console.log(вложенный.flat(2)); // [1, 2, 3, 4, 5, 6] (2 уровня)
// every — все ли элементы удовлетворяют условию?
console.log([2, 4, 6].every(n => n % 2 === 0)); // true (все чётные)
console.log([2, 3, 6].every(n => n % 2 === 0)); // false (3 нечётное)
// some — хотя бы один удовлетворяет условию?
console.log([1, 3, 5].some(n => n > 4)); // true (5 > 4)
Практический пример: система оценок
const оценки = [85, 92, 78, 65, 90, 55, 88, 72, 95, 60];
// Статистика:
const количество = оценки.length;
const сумма = оценки.reduce((с, о) => с + о, 0);
const среднее = (сумма / количество).toFixed(1);
const максимум = Math.max(...оценки);
const минимум = Math.min(...оценки);
// Категории:
const отличников = оценки.filter(о => о >= 90).length;
const провалили = оценки.filter(о => о < 60).length;
console.log(`📊 Статистика класса:`);
console.log(` Студентов: ${количество}`);
console.log(` Средняя оценка: ${среднее}`);
console.log(` Лучшая: ${максимум}, Худшая: ${минимум}`);
console.log(` Отличников: ${отличников}`);
console.log(` Не сдали: ${провалили}`);
Типичные ошибки новичков
- Индекс начинается с 1. Нет! С 0. Первый элемент:
массив[0], последний:массив[массив.length - 1]. - Мутация при копировании.
let б = ане копирует массив — оба указывают на один и тот же массив! Для копии используйтеlet б = [...а]илиа.slice(). - Сортировка чисел по умолчанию.
[10, 9, 2].sort()даст[10, 2, 9](сортировка как строки). Всегда передавайте компаратор:.sort((а, б) => а - б). - forEach не возвращает новый массив. Если нужен новый массив — используйте
map, неforEach. - map/filter не меняют оригинал. Они создают НОВЫЙ массив. Если не сохранить в переменную — результат потеряется.
Итого
В этом уроке мы узнали:
- Массив — упорядоченный список элементов, доступных по числовому индексу (с 0).
- push/pop — добавить/удалить с конца. unshift/shift — добавить/удалить с начала.
- splice — универсальный метод добавления/удаления/замены в любом месте.
- forEach — перебрать каждый элемент (ничего не возвращает).
- map — создать новый массив, трансформируя каждый элемент.
- filter — создать новый массив из элементов, прошедших условие.
- reduce — свернуть массив в одно значение, накапливая результат.
- Spread (...) — развернуть массив: копирование, объединение, передача как аргументов.
В следующем уроке мы изучим объекты и классы — способ группировать связанные данные и действия.