Неизменяемость данных
Неизменяемость (immutability) — подход, при котором данные не меняют на месте, а создают обновлённую копию. Так проще искать ошибки: объект не может внезапно измениться из-за кода в другом конце программы. На этом подходе построены React и многие другие библиотеки.
Примитивы не меняются
Строки, числа и другие примитивы неизменяемы. Методы строк не трогают исходную строку, а возвращают новую:
const word = 'кот';
const upper = word.toUpperCase();
console.log(word); // кот
console.log(upper); // КОТОбъекты передаются по ссылке
С объектами и массивами иначе. Переменная хранит не сам объект, а ссылку на него, поэтому две переменные могут указывать на один объект:
const anna = { score: 1 };
const player = anna;
player.score = 2;
console.log(anna.score); // 2const здесь не помогает: он запрещает записать в переменную другое значение, но не мешает менять содержимое объекта.
Обновление через копию
Вместо изменения создают новый объект с нужными правками. Оператор spread ... копирует старые свойства, а следом пишут новые:
const user = { name: 'Анна', age: 28 };
const older = { ...user, age: 29 };
console.log(user.age); // 28
console.log(older.age); // 29Для массивов так же: [...list, item] вместо list.push(item).
Методы массивов без изменения
Некоторые методы массивов меняют исходный массив. Для них есть пары, которые возвращают копию:
| Меняет массив | Возвращает копию |
|---|---|
sort() | toSorted() |
reverse() | toReversed() |
splice() | toSpliced() |
arr[i] = x | arr.with(i, x) |
const numbers = [3, 1, 2];
const sorted = numbers.toSorted();
console.log(numbers.join(', ')); // 3, 1, 2
console.log(sorted.join(', ')); // 1, 2, 3Object.freeze
Object.freeze() «замораживает» объект: его свойства больше нельзя изменить, добавить или удалить. Попытка молча игнорируется, а в строгом режиме вызывает ошибку:
const config = Object.freeze({ currency: '€' });
config.currency = '$';
console.log(config.currency); // €Заморозка неглубокая
Object.freeze() действует только на верхний уровень: вложенные объекты и массивы остаются изменяемыми. Полную независимую копию со всеми вложенными данными делает функция structuredClone():
const order = { items: ['чай'] };
const copy = structuredClone(order);
copy.items.push('кофе');
console.log(order.items.length); // 1
console.log(copy.items.length); // 2Spread копирует тоже только верхний уровень: в копии { ...order } массив items был бы тем же самым.
Частая ошибка
Думать, что sort() возвращает отсортированную копию. Он сортирует сам массив, и исходный порядок теряется:
const prices = [30, 10, 20];
const sorted = prices.sort((a, b) => a - b);
console.log(prices.join(', ')); // 10, 20, 30
console.log(sorted === prices); // trueЕсли исходный массив ещё нужен, используйте toSorted() или сортируйте копию: [...prices].sort().