Какие подводные камни есть при использовании ссылочных типов в JavaScript кроме сравнения
Что проверяют
Понимаете ли вы, что переменная хранит ссылку, и видите ли, где это ломает код на практике: в функциях, копиях, состоянии фреймворка. Следом обычно просят сделать глубокую копию или объяснить, почему компонент не перерисовался после изменения массива.
Как отвечать
Объекты, массивы, функции, Map и Set передаются по ссылке, точнее, ссылка копируется по значению. Отсюда реальные проблемы:
- мутация чужих данных: функция, которая делает
arr.push()или меняет поле аргумента, меняет объект у вызывающего кода. Так же ведут себяsort(),reverse(),splice(); с ES2023 есть неизменяющиеtoSorted(),toReversed(),toSpliced(),with(); - поверхностная копия: спред и
Object.assign()копируют только верхний уровень, вложенные объекты остаются общими; - глубокая копия с потерями:
JSON.parse(JSON.stringify(x))теряетundefinedи функции, превращаетDateв строку,Mapв пустой объект и падает на циклических ссылках.structuredClone()справляется сDate,Mapи циклами, но бросает ошибку на функциях и не сохраняет прототип класса; constиObject.freeze(): первое запрещает переприсвоить переменную, а не менять объект, второе замораживает только верхний уровень;- общий объект по ошибке:
Array(3).fill({})кладёт в массив три ссылки на один объект; - React и мемоизация: изменили массив в состоянии на месте, ссылка та же,
Object.isне видит разницы, перерисовки нет. Обратная беда: новый объект на каждый рендер в зависимостяхuseEffectили в пропсахmemoзапускает эффект и рендер каждый раз; - утечки памяти: объект живёт, пока на него ссылается замыкание, подписка или кэш в
Map. Для кэша по объекту подходитWeakMap.
Где спотыкаются
Называют спред глубокой копией и удивляются, что правка вложенного поля задела оригинал.
Считают JSON.stringify универсальным клонированием и не знают про structuredClone(), который есть во всех актуальных браузерах и Node.js.
Думают, что const делает объект неизменяемым, мутируют состояние React напрямую, а потом ищут, почему интерфейс не обновился.