Новые методы массивов при копировании в JavaScript в ECMAScript 2023
JavaScript

Новые методы массивов при копировании в JavaScript в ECMAScript 2023

Razilator

Недавно была завершена спецификация ECMAScript 2023. Она включает в себя некоторые новые методы объекта Array, которые помогут сделать программы на JavaScript более предсказуемыми и удобными для поддержки.

Методы toSorted, toReversed, toSpliced и with позволяют выполнять операции над массивами, не изменяя данные на месте, а создавая копию и изменяя эту копию массива.

Мутация и побочные эффекты

Объект Array всегда имел некоторые проблемы. Методы sort, reverse и splice, изменяют массив на месте. Другие методы, такие как concat, map и filter, создают копию массива и затем работают с копией. Когда вы выполняете операцию над объектом, которая мутирует его, это является побочным эффектом и может вызвать неожиданное поведение в других местах вашей системы.

index.js
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 возвращает новый отсортированный массив.

index.js
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 для строк), которая будет давать ожидаемые результаты.

index.js
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]
index.js
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 возвращает новый массив, отсортированный в обратном порядке.

index.js
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 возвращает новый массив без удаленных элементов и с добавленными элементами. Вот как это работает:

index.js
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 к исходному массиву, вот так:
index.js
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 является эквивалентом копирования использования квадратных скобок для изменения одного элемента массива. Таким образом, вместо прямого изменения массива, как показано ниже:

index.js
const languages = ['Russian', 'English', 'Deutsch'];
languages[2] = 'Chinese';
console.log(languages); // ['Russian', 'English', 'Chinese']

Вы можете скопировать массив и внести изменение:

index.js
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 поддерживают все четыре метода, и существуют полифиллы и шимы для платформ, которые пока не имеют поддержки.

;