Методы массивов в JavaScript: Исчерпывающий гид для разработчиков
Работа с массивами — базовая и ежедневная задача для JS-разработчика. Понимание нюансов каждого метода, включая их стоимость и побочные эффекты, напрямую влияет на производительность и надежность кода. Давайте структурируем знания, выходя за рамки простого перечисления.
Мутирующие методы (Mutating Methods)
Эти методы изменяют исходный массив. Важно помнить об этом, чтобы избежать непреднамеренных сайд-эффектов.
push(...items) / unshift(...items)
Добавляют элементы в конец/начало. Возвращают новую длину массива.
const arr = [1, 2];
const newLength = arr.push(3); // arr = [1, 2, 3], newLength = 3
Примечание:
-
unshiftдобавляет все аргументы сразу, а не по одному. Они становятся частью массива в том порядке, в котором переданы.arr.unshift(0, -1); // arr = [-1, 0, 1, 2, 3] -
Критически важно:
unshift— дорогой оператор для больших массивов, так как он требует сдвига всех существующих элементов и пересчета их индексов.pushработает значительно быстрее, так как просто добавляет элементы в конец.
pop() / shift()
Удаляют и возвращают последний/первый элемент. Если массив пуст, возвращают undefined.
const last = arr.pop(); // last = 3, arr = [-1, 0, 1, 2]
const first = arr.shift(); // first = -1, arr = [0, 1, 2]
Примечание:
Аналогично unshift, метод shift требует сдвига всех элементов, начиная с индекса 1, влево, что делает его затратным для больших массивов. pop — быстрый.
splice(start[, deleteCount, ...items])
"Швейцарский нож" для работы с массивами. Изменяет массив, начиная с индекса start, удаляя deleteCount элементов и вставляя ...items на их место. Возвращает массив удаленных элементов.
const arr = ['a', 'b', 'c', 'd'];
const removed = arr.splice(1, 2, 'X', 'Y');
// arr = ['a', 'X', 'Y', 'd']
// removed = ['b', 'c']
Примечание:
- Отрицательный
startотсчитывается с конца. - Если
deleteCountопущен, удаляет все элементы до конца. - Можно использовать для вставки без удаления:
arr.splice(2, 0, 'Z'); - Производительность: Это мутирующий метод, который может быть затратным, так как после точки вставки/удаления требует сдвига оставшихся элементов и пересчета их индексов.
sort([compareFunction])
Сортирует массив на месте. Без функции сравнения преобразует элементы в строки и сортирует лексикографически.
const nums = [10, 2, 1];
nums.sort(); // [1, 10, 2] - сюрприз!
nums.sort((a, b) => a - b); // [1, 2, 10] - правильно для чисел
Примечание:
-
Для сложных структур используйте явную
compareFunction.const users = [{name: 'Bob', age: 30}, {name: 'Alice', age: 25}]; users.sort((a, b) => a.age - b.age); // Сортировка по возрасту -
Стабильность: Начиная с ES2019, метод
sortв JavaScript является стабильным. Это означает, что элементы, сравнение которых приводит к равенству, сохраняют свой исходный относительный порядок. - Производительность: Эффективность алгоритма сортировки зависит от браузера (движка), но в большинстве случаев используется высокооптимизированный алгоритм (Timsort в V8).
reverse() / fill(value[, start[, end]])
reverse переворачивает массив. fill заполняет массив одним значением от start до end (не включая).
const arr = [1, 2, 3];
arr.fill(0, 1); // [1, 0, 0]
arr.reverse(); // [0, 0, 1]
Примечание:
fill отлично подходит для инициализации массивов фиксированной длины.
const newArray = new Array(5).fill(0); // [0, 0, 0, 0, 0]
Не мутирующие методы (Non-Mutating Methods)
Создают новый массив или возвращают новое значение, не трогая исходный.
concat(...items)
Объединяет массивы. Принимает как массивы, так и простые значения.
const arr1 = [1, 2];
const arr2 = [3, 4];
const newArr = arr1.concat(arr2, 5); // [1, 2, 3, 4, 5]
// arr1 и arr2 не изменены
Примечание:
В современном JS часто заменяется на Spread Operator:
const newArr = [...arr1, ...arr2, 5]; // Аналогичный результат
Однако concat может быть чуть более производительным при объединении именно двух массивов, в то время как spread syntax более читаем и универсален.
slice([start[, end]])
Возвращает новый массив, содержащий копию элементов от start до end (не включая). Отрицательные индексы работают с конца.
const arr = [10, 20, 30, 40];
const subArr = arr.slice(1, 3); // [20, 30]
const copy = arr.slice(); // Поверхностная копия массива
Примечание:
Создает поверхностную копию (shallow copy). Если массив содержит объекты, они не будут клонированы, и в новом массиве будут ссылки на те же объекты.
join([separator])
Склеивает элементы в строку. separator по умолчанию - запятая.
['2024', '12', '31'].join('-'); // "2024-12-31"
indexOf(item[, from]) / lastIndexOf() / includes(item[, from])
Поиск в массиве. indexOf и lastIndexOf возвращают индекс (или -1), includes — булево значение.
const arr = [1, 2, 3, 2, 1];
arr.indexOf(2); // 1
arr.lastIndexOf(2); // 3
arr.includes(4); // false
Примечание:
-
Для поиска объектов или
NaNэти методы не подходят (используйтеfind).[NaN].indexOf(NaN); // -1 (не находит) [NaN].includes(NaN); // true (находит) includesправильно обрабатываетNaN, в отличие отindexOf.- Используют строгое сравнение (
===).
find(predicate) / findIndex(predicate)
Ищут элемент/индекс с помощью функции-предиката. Возвращают первый удовлетворяющий элемент или его индекс.
const users = [{id: 1}, {id: 2}, {id: 1}];
const user = users.find(item => item.id === 1); // {id: 1}
const index = users.findIndex(item => item.id === 1); // 0
Примечание:
Предикат-функция вызывается для каждого элемента до тех пор, пока не будет найдено совпадение. Это может быть эффективнее filter, если нужен только первый элемент.
Итерационные методы (Iteration Methods)
Основа функционального программирования в JS. Не меняют исходный массив (кроме forEach, который может изменять элементы, если они мутабельны).
forEach(callback)
Просто выполняет функцию для каждого элемента. Возвращает undefined.
['a', 'b', 'c'].forEach((letter, index) => {
console.log(`${index}: ${letter}`);
});
Примечание:
- Нельзя прервать цикл
breakилиreturnиз колбэка. Для досрочного прерывания используйте обычныйforилиfor..of. - В отличие от
for, не создает отдельную область видимости для блока, если используется стрелочная функция.
map(callback)
Создает новый массив, применяя функцию к каждому элементу. Размер итогового массива всегда равен исходному.
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2); // [2, 4, 6]
Примечание:
Всегда возвращает новый массив. Исходный массив не изменяется. Это один из самых часто используемых и полезных методов.
filter(callback)
Создает новый массив со всеми элементами, прошедшими проверку (предикат вернул true).
const nums = [1, 2, 3, 4, 5];
const even = nums.filter(n => n % 2 === 0); // [2, 4]
Примечание:
Если ни один элемент не подошел, возвращает пустой массив.
reduce(callback[, initialValue]) / reduceRight
Сворачивает массив в одно значение. callback(accumulator, currentItem, index, array).
const sum = [1, 2, 3, 4].reduce((acc, curr) => acc + curr, 0); // 10
Примечание:
Всегда передавайте initialValue для надежности. Без нее:
accберется как первый элемент массива.- Итерация стартует со второго элемента.
- Вызов на пустом массиве без
initialValueвыброситTypeError.
some(callback) / every(callback)
Проверяют, удовлетворяет ли хотя бы один (some) или все (every) элементы условию. Имеют ленивое выполнение (short-circuit).
const hasNegative = [1, -2, 3].some(n => n < 0); // true (остановится на -2)
const allPositive = [1, 2, 3].every(n => n > 0); // true
Новые методы (ES6+)
flat([depth]) / flatMap(callback)
flat "поднимает" вложенные массивы на указанный depth (по умолчанию 1). flatMap эквивалентен map().flat(1), но более эффективен.
const arr = [1, [2, [3]]];
arr.flat(); // [1, 2, [3]]
arr.flat(2); // [1, 2, 3]
const phrases = ['hello world', 'goodbye moon'];
const words = phrases.flatMap(phrase => phrase.split(' '));
// ['hello', 'world', 'goodbye', 'moon']
Примечание:
flatMap позволяет отфильтровать элементы на этапе маппинга, возвращая пустой массив для нежелательных элементов.
const arr = [1, 2, 3, 4];
const result = arr.flatMap(x => x % 2 === 0 ? [] : [x * 2]);
// [2, 6] (удвоили только нечетные, отфильтровав четные)
Array.from(iterable[, mapFn])
Создает массив из массивоподобного или итерируемого объекта (например, NodeList, arguments, String, Set).
Array.from('foo'); // ['f', 'o', 'o']
Array.from([1, 2, 3], x => x * x); // [1, 4, 9] // Использование mapFn
Примечание:
Второй аргумент mapFn позволяет сначала создать массив, а затем сразу его обработать, что часто эффективнее цепочки Array.from().map().
Array.isArray(value)
Надежная проверка, является ли значение массивом (в отличие от typeof, который для массива возвращает 'object').
Array.isArray([]); // true
Array.isArray({}); // false
Array.isArray(Array.prototype); // true (да, это тоже массив!)
Заключение
Правильный выбор метода массива — это не только вопрос синтаксиса, но и:
- Производительности: Понимание стоимости операций (например,
unshift/shiftvspush/pop) критично для работы с большими данными. - Иммутабельности: Использование не мутирующих методов делает код предсказуемее, особенно в реактивных фреймворках (React, Vue).
- Читаемости: Цепочка
map().filter().reduce()часто яснее, чем один большой циклfor, и меньше подвержена ошибкам.
Комбинируйте эти методы, осознавая их внутреннюю работу, чтобы создавать не только мощные и выразительные, но и высокопроизводительные конструкции для обработки данных.