Новые методы массивов при копировании в JavaScript в ECMAScript 2023
Недавно была завершена спецификация ECMAScript 2023. Она включает в себя некоторые новые методы объекта Array
, которые помогут сделать программы на JavaScript более предсказуемыми и удобными для поддержки.
Методы toSorted
, toReversed
, toSpliced
и with
позволяют выполнять операции над массивами, не изменяя данные на месте, а создавая копию и изменяя эту копию массива.
Мутация и побочные эффекты
Объект Array
всегда имел некоторые проблемы. Методы sort
, reverse
и splice
, изменяют массив на месте. Другие методы, такие как concat
, map
и filter
, создают копию массива и затем работают с копией. Когда вы выполняете операцию над объектом, которая мутирует его, это является побочным эффектом и может вызвать неожиданное поведение в других местах вашей системы.
const languages = ['Russian', 'English', 'Deutsch'];
const reversed = languages.reverse();
console.log(reversed); // [ 'Deutsch', 'English', 'Russian' ]
console.log(languages); // [ 'Deutsch', 'English', 'Russian' ]
console.log(Object.is(languages, reversed)); // true
Как видите, оригинальный массив был изменен и даже если мы присвоили результат метода reverse()
массива новой переменной, обе переменные просто указывают на один и тот же массив.
Новые методы ECMAScript 2023
Array.prototype.toSorted
Метод toSorted
возвращает новый отсортированный массив.
const languages = ['Russian', 'English', 'Deutsch'];
const sorted = languages.toSorted();
console.log(sorted); // => [ 'Deutsch', 'English', 'Russian' ]
console.log(languages); // => [ 'Russian', 'English', 'Deutsch' ]
console.log(Object.is(languages, sorted)); // false
Вам все равно нужно быть осторожными, если вы сортируете числа или строки с акцентированными символами. Убедитесь, что вы предоставляете функцию сравнения (например, localeCompare
для строк), которая будет давать ожидаемые результаты.
const numbers = [5, 3, 10, 7, 1];
const sorted = numbers.toSorted();
console.log(sorted); // [1, 10, 3, 5, 7]
const sortedCorrectly = numbers.toSorted((a, b) => a - b);
console.log(sortedCorrectly); // [1, 3, 5, 7, 10]
const strings = ["abc", "äbc", "def"];
const sorted = strings.toSorted();
console.log(sorted); // [ 'abc', 'def', 'äbc' ]
const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b));
console.log(sortedCorrectly); // [ 'abc', 'äbc', 'def' ]
Array.prototype.toReversed
Использование метода toReversed
возвращает новый массив, отсортированный в обратном порядке.
const languages = ['Russian', 'English', 'Deutsch'];
const reversed = languages.toReversed();
console.log(reversed); // ['Deutsch', 'English', 'Russian']
console.log(Object.is(languages, reversed)); // false
Теперь вы можете использовать toReversed
или toSorted
, чтобы скопировать массив и изменять копию вместо исходного.
Array.prototype.toSpliced
Метод toSpliced
немного отличается от своей исходной версии splice
.
Метод splice
изменяет существующий массив путем удаления и добавления элементов по указанному индексу и возвращает массив, содержащий удаленные элементы из массива.
toSpliced
возвращает новый массив без удаленных элементов и с добавленными элементами. Вот как это работает:
const languages = ['Russian', 'English', 'Deutsch'];
const spliced = languages.toSpliced(2, 1, 'Chinese', 'Spanish');
console.log(spliced); // ['Russian', 'English', 'Chinese', 'Spanish']
console.log(Object.is(languages, spliced)); // false
Если вы используете splice
из-за его возвращаемого значения, то toSpliced
не будет полностью совместимым заменителем. Если вы хотите получить удаленные элементы без изменения исходного массива, то вам следует использовать метод slice
.
К сожалению, у splice
и slice
разные аргументы.
splice
принимает индекс и количество элементов после этого индекса, которые нужно удалить.slice
принимает два индекса - начальный и конечный. Если вы хотите использоватьtoSpliced
вместоsplice
, но также получить удаленные элементы, вы можете применитьtoSpliced
иslice
к исходному массиву, вот так:
const languages = ['Russian', 'English', 'Deutsch'];
const startDeletingAt = 2;
const deleteCount = 1;
const spliced = languages.toSpliced(startDeletingAt, deleteCount, 'Chinese', 'Spanish');
const removed = languages.slice(startDeletingAt, startDeletingAt + deleteCount);
console.log(spliced); // ['Russian', 'English', 'Chinese', 'Spanish']
console.log(removed); // ['Deutsch']
Array.prototype.with
Метод with
является эквивалентом копирования использования квадратных скобок для изменения одного элемента массива. Таким образом, вместо прямого изменения массива, как показано ниже:
const languages = ['Russian', 'English', 'Deutsch'];
languages[2] = 'Chinese';
console.log(languages); // ['Russian', 'English', 'Chinese']
Вы можете скопировать массив и внести изменение:
const languages = ['Russian', 'English', 'Deutsch'];
const updated = languages.with(2, 'Chinese');
console.log(updated); // ['Russian', 'English', 'Chinese']
console.log(languages); // ['Russian', 'English', 'Deutsch']
Обычный объект массива не единственный, который получает преимущества от этих новых методов. Вы также можете использовать toSorted
, toReversed
и with
с любым TypedArray
. Это включает в себя все типизированные массивы, от Int8Array
до BigUint64Array
. Типизированные массивы не имеют метода splice
, поэтому для них не предусмотрен соответствующий метод toSpliced
.
Поддержка
Хотя спецификация ECMAScript 2023 очень новая, уже существует хорошая поддержка для этих новых методов массива. Chrome 110
, Safari 16.3
, Node.js 20
и Deno 1.31
поддерживают все четыре метода, и существуют полифиллы и шимы для платформ, которые пока не имеют поддержки.