Обновления неизменяемого массива с помощью Array.prototype.with

В браузерах недавно появился новый совместимый метод, который можно вызывать для массивов: Array.prototype.with() .

Browser Support

  • Chrome: 110.
  • Edge: 110.
  • Firefox: 115.
  • Сафари: 16.

Source

В этой статье рассматривается принцип работы данного метода и способы его использования для обновления массива без изменения исходного массива.

Введение в Array.prototype.with(index, value)

Метод Array.prototype.with(index, value) возвращает копию массива, к которому он применяется, с index установленным на новое предоставленное вами value .

В следующем примере показан массив возрастов. Вам нужно создать новую копию массива, изменив второй возраст с 15 на 16:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, 16);
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Разберем код: ages.with(...) возвращает копию переменной ages без изменения исходного массива. ages.with(1, …) ` заменяет второй элемент ( index = 1 ). ages.with(1, 16) присваивает второму элементу значение 16 .

Таким образом вам удалось создать новую копию массива с внесенными изменениями.

Это очень полезно, когда нужно убедиться, что исходный массив остаётся неизменным, и в этой статье рассматриваются некоторые примеры его использования. Но пока давайте посмотрим, что бы произошло, если бы вы использовали скобочную запись:

const ages = [10, 15, 20, 25];

const newAges = ages;
newAges[1] = 16;
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 16, 20, 25] (Also changed 🙁)

Как видите, в этом примере также была изменена переменная ` ages . Это потому, что когда вы присваиваете ages = newAges , JavaScript не копирует массив, а создает ссылку на другой массив. Таким образом, любое изменение в одном повлияет и на другой, поскольку оба массива указывают на один и тот же массив.

Array.prototype.with() и неизменяемость

Неизменяемость данных лежит в основе многих фронтенд-библиотек и фреймворков, например: React (и Redux) и Vue.

Кроме того, другие библиотеки и фреймворки не обязательно требуют неизменяемости, но поощряют её для повышения производительности: Angular и Lit.

Поэтому разработчикам часто приходилось использовать другие методы, которые возвращали копии массивов, что снижало читаемость кода:

const ages = [10, 15, 20, 25];

const newAges = ages.map((age, index) => {
    if (index === 1) {
         return 16;
    }
    return age;
});

console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (Remains unchanged)

Вот пример на Codepen, демонстрирующий, как можно использовать .with() в React в сочетании с `useState` для неизменяемого обновления массива элементов:

Поскольку метод .with() возвращает копию массива, вы можете объединять несколько вызовов .with() или даже другие методы работы с массивами. Следующий пример демонстрирует увеличение второго и третьего значений возраста из массива:

const ages = [10, 15, 20, 25];

const newAges = ages.with(1, ages[1] + 1).with(2, ages[2] + 1)

console.log(newAges); // [10, 16, 21, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)

Другие новые неизменяемые методы

Недавно стали совместимы еще три метода:

Согласно MDN, эти три метода являются копирующими версиями своих аналогов. Эти методы также могут использоваться там, где ожидается или предпочтительна неизменяемость.

В заключение, неизменяемые обновления можно проще реализовать в JavaScript с помощью одного из четырех методов, представленных в этой статье. В частности, метод .with() упрощает обновление одного элемента массива без изменения исходного массива.