Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11

Безопасные методы работы с массивами в JavaScript

Безопасные методы работы с массивами в JavaScript: toSorted(), toReversed() и toSpliced() вместо мутирующих sort(), reverse() и splice(). Примеры использования в React, сравнение методов и поддержка браузерами. Как писать чистый код без побочных эффектов.

96 открытий2К показов
Безопасные методы работы с массивами в JavaScript

Это перевод статьи Мэта Смитта (Matt Smith) — веб-разработчика, фронтенд-инженера и UX-дизайнера.

Есть веская причина, по которой многие разработчики задумываются, прежде чем использовать в JavaScript методы .sort(), .reverse() или .splice(): эти методы изменяют исходный массив. Такой побочный эффект может привести к скрытым, трудноуловимым ошибкам — особенно в приложениях с общим или реактивным состоянием.

Хорошая новость в том, что за последние пару лет в JavaScript появились новые методы работы с массивами, которые делают этот процесс более безопасным и чистым, полностью избегая мутаций:

  • toSorted()
  • toReversed()
  • toSpliced()

Эти методы возвращают копии массивов, вместо того чтобы изменять оригинал. Это небольшое, но важное обновление синтаксиса — особенно для разработчиков на React, где неизменяемость данных (immutability) — ключ к правильному управлению состоянием.

Проблема методов, изменяющих массив «на месте»

В JavaScript традиционные методы, такие как .sort(), .reverse() и .splice(), модифицируют сам массив, на котором были вызваны:

			const numbers = [3, 1, 2];
numbers.sort(); // Mutates the array 😬
console.log(numbers); // [1, 2, 3]
		

В таких фреймворках, как React, это может привести к непредсказуемому поведению при обновлении состояния, поскольку прямое изменение массивов не вызывает повторный рендер.

Сравнение старых и новых методов

  • Для сортировки вместо arr.sort(), который изменяет исходный массив, теперь можно использовать arr.toSorted(), возвращающий новый.
  • Для обратного порядка вместо arr.reverse() следует применять arr.toReversed(), чтобы сохранить оригинал.
  • Для удаления или вставки элементов вместо arr.splice() можно использовать arr.toSpliced(), который создаёт копию массива с изменениями, не трогая исходный.

Эти новые методы работают так же, как и их «изменяющие» аналоги, но вместо модификации исходного массива возвращают новый.

⚠️ Важно: это поверхностные копии, поэтому если массив содержит объекты, сами объекты останутся теми же ссылками, а не новыми экземплярами.

Решение: безопасные, не изменяющие оригинал методы

В стандарте ES2023 появились новые версии привычных методов массивов, которые не изменяют исходный массив:

toSorted() — создаёт отсортированную копию массива, не затрагивая оригинал.

			const numbers = [3, 1, 2];
const sorted = numbers.toSorted();

console.log(sorted);  // [1, 2, 3]
console.log(numbers); // [3, 1, 2] ✅

// ‼️ Contrast: .sort() mutates the array
numbers.sort();
console.log(numbers); // [1, 2, 3] ❌

		

Вы также можете передать собственную функцию сравнения — точно так же, как в методе .sort():

			const users = [
  { name: 'Kristen', age: 36 },
  { name: 'David', age: 34 },
];

const byAge = users.toSorted((a, b) => a.age - b.age);

console.log(byAge); // [ { name: 'David', age: 34 }, { name: 'Kristen', age: 36 } ]
console.log(users); // [ { name: 'Kristen', age: 36 }, { name: 'David', age: 34 } ] ✅

		

toReversed() — возвращает обратную копию массива, не изменяя исходный порядок элементов в оригинале.

			const names = ['Kristen', 'David', 'Ben'];
const reversed = names.toReversed();

console.log(reversed); // ['Ben', 'David', 'Kristen']
console.log(names);    // ['Kristen', 'David', 'Ben'] ✅

// ‼️ Contrast: .reverse() mutates the array
names.reverse();
console.log(names);    // ['Ben', 'David', 'Kristen'] ❌

		

Отлично подходит для случаев, когда нужно вывести список в обратном порядке, не изменяя исходный массив.

toSpliced() — это более безопасная альтернатива методу .splice(). Она возвращает новый массив с добавленными или удалёнными элементами, не затрагивая оригинал.

			const items = ['a', 'b', 'c', 'd'];

// Remove 1 item at index 1
const withoutB = items.toSpliced(1, 1);

console.log(withoutB); // ['a', 'c', 'd']
console.log(items);    // ['a', 'b', 'c', 'd'] ✅

// Add new element at index 2
const withX = items.toSpliced(2, 0, 'x');
console.log(withX); // ['a', 'b', 'x', 'c', 'd']

// ‼️ Contrast: .splice() mutates the array
const items2 = ['a', 'b', 'c', 'd'];
const removed = items2.splice(1, 1);
console.log(removed);  // ['b']
console.log(items2);   // ['a', 'c', 'd'] ❌

		

Напоминание: метод .splice() возвращает удалённые элементы, а .toSpliced() — обновлённый массив.

Почему это важно в React

В React неизменяемость данных — ключевой принцип, который обеспечивает обновление компонентов и предсказуемость состояния.

			// ❌ Mutating state directly (bad)
state.items.sort(); // No re-render

// ✅ Using toSorted (good)
const sortedItems = state.items.toSorted();
setState({ items: sortedItems }); // Triggers re-render

		

Новые методы позволяют работать с массивами как с неизменяемыми структурами данных, не прибегая к использованию structuredClone() или сложных обходных решений для глубокой копии.

Практический пример: сортировка задач в React

Вот как можно использовать toSorted() или toReversed() в компоненте, чтобы безопасно отображать динамические списки, не изменяя исходные данные:

			function TaskList({ tasks }) {
  // Show most recent tasks first
  const recentFirst = tasks?.toReversed() ?? [];

  return (
    <ul>
      {recentFirst.map((task) => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

		

Такой подход избегает изменения массива tasks, что особенно важно, если он передан через props или вычисляется из state — в противном случае могут возникнуть ошибки. Кроме того, использование операторов опциональной цепочки (?.) и объединения с null (??) помогает предотвратить сбои, если tasks окажется undefined.

И это ещё не всё! Оба этих оператора — опциональная цепочка и nullish coalescing — улучшения синтаксиса, делающие код более надёжным и устойчивым к ошибкам во время выполнения, приближая его к современному стандарту JavaScript.

Маленькое изменение в синтаксисе — большое улучшение

Эти методы не требуют нового подхода к мышлению — это просто безопасные, неизменяемые версии уже привычных вам инструментов. Если вы работаете в современной среде (или используете сборщики вроде Babel или SWC), вы можете начать применять их уже сегодня.

Поддержка браузерами

Методы toSorted(), toReversed() и toSpliced() поддерживаются во всех современных средах:

✅ Chrome / Edge — с версии 110+

✅ Safari — с версии 16+

✅ Firefox — с версии 115+

✅ Node.js — с версии 20+

Для старых окружений можно использовать полифил, например, из библиотеки core-js.

Основные выводы

Метод .toSorted() возвращает отсортированную копию массива, не изменяя оригинал. Метод .toReversed() создаёт новый массив в обратном порядке, также без мутаций исходного. Метод .toSpliced() возвращает изменённую копию массива — с добавленными или удалёнными элементами, но оригинальный массив остаётся нетронутым.

ES2023 принёс не только заметные обновления вроде опциональной цепочки и top-level await, но и такие, казалось бы, мелкие улучшения, которые делают код чище, понятнее и безопаснее. Попробуйте использовать новые методы в своих проектах — и вы больше не будете смотреть на .sort() с тем же доверием.

Следите за новыми постами
Следите за новыми постами по любимым темам
96 открытий2К показов