Почему новый объект не изменяет исходный объект при передаче в функцию в JavaScript
Что проверяют
Понимаете ли вы, что передаётся копия ссылки, и различаете ли присваивание и мутацию.
Как отвечать
Аргументы всегда передаются по значению. Для объекта значением является ссылка, поэтому в функцию попадает её копия. Копия указывает на тот же объект, но сама является отдельной переменной.
Отсюда три разных случая:
function reassign(obj) {
obj = { name: "новый" }; // меняем локальную копию ссылки
}
function mutate(obj) {
obj.name = "новый"; // меняем объект по ссылке
}
const user = { name: "старый" };
reassign(user); // user.name === "старый"
mutate(user); // user.name === "новый"Присваивание параметру нового объекта перенаправляет только локальную переменную. Исходная переменная снаружи по-прежнему указывает на прежний объект. Изменение свойств идёт по ссылке и видно всем, кто на этот объект смотрит.
Такую модель часто называют передачей ссылки по значению, и это точнее, чем просто «по ссылке». В языках с настоящей передачей по ссылке первый пример изменил бы и внешнюю переменную.
Практические следствия:
- функция не может заменить переданный объект, она может только изменить его содержимое или вернуть новый;
- методы вроде развёртывания свойств возвращают новый объект и потому безопасны;
- методы массива делятся на две группы: одни изменяют массив на месте, другие возвращают новый. Смешение их и даёт большую часть неожиданных изменений;
- в неизменяемом стиле, принятом в React, объект вообще не меняют, а создают новый. Тогда обнаружение изменений сводится к сравнению ссылок.
Полезно упомянуть, что развёртывание свойств копирует поверхностно: вложенные объекты останутся общими. Для полной копии есть штатная функция структурного клонирования.
Где спотыкаются
Говорят, что объекты передаются по ссылке, и не могут объяснить первый пример.
Ждут, что присваивание внутри функции изменит внешнюю переменную.
Путают методы, изменяющие массив, с теми, что возвращают новый.
Считают поверхностную копию полной.