Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Глубокое копирование (Deep Copy) в работе
Да, регулярно используюглубокое копирование. Это критично при работе с данными и состоянием в JavaScript.
Когда это нужно
1. Иммутабельность в React/Redux
// Плохо — мутируем оригинальный объект
const handleUpdate = (userId, newName) => {
const user = users[userId];
user.name = newName; // Мутация! React не заметит изменения
};
// Хорошо — создаём новый объект (shallow copy)
const handleUpdate = (userId, newName) => {
const updatedUsers = {
...users,
[userId]: { ...users[userId], name: newName }
};
setUsers(updatedUsers);
};
2. Глубокие структуры (nested objects)
// Shallow copy не подходит для вложенных объектов
const user = {
id: 1,
name: 'John',
profile: {
bio: 'Developer',
address: {
city: 'NYC'
}
}
};
// Shallow copy
const copied1 = { ...user };
copied1.profile.address.city = 'LA'; // Меняет оригинал!
// Deep copy — нужен для вложенных структур
const copied2 = JSON.parse(JSON.stringify(user));
copied2.profile.address.city = 'LA'; // Оригинал не меняется
Способы глубокого копирования
1. JSON метод (просто, но с минусами)
const deepCopy = (obj) => JSON.parse(JSON.stringify(obj));
// Работает для простых данных
const user = { name: 'John', age: 30 };
const copy = deepCopy(user);
// ПРОБЛЕМЫ:
// 1. Теряет функции
const obj = {
name: 'John',
greet() { return 'Hello'; }
};
const copy = deepCopy(obj);
copy.greet(); // TypeError: copy.greet is not a function
// 2. Теряет undefined
const obj = { a: 1, b: undefined };
const copy = deepCopy(obj);
console.log(copy.b); // undefined не скопирован!
// 3. Теряет Date
const obj = { date: new Date('2025-01-01') };
const copy = deepCopy(obj);
console.log(typeof copy.date); // string (не Date!)
// 4. Теряет Map/Set/Symbol
const obj = { map: new Map() };
const copy = deepCopy(obj); // Map преобразуется в {}
2. Рекурсивный способ (надёжнее)
const deepCopy = (obj) => {
// Примитивы
if (obj === null || typeof obj !== 'object') {
return obj;
}
// Массивы
if (Array.isArray(obj)) {
return obj.map(item => deepCopy(item));
}
// Объекты
const copied = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
copied[key] = deepCopy(obj[key]);
}
}
return copied;
};
const user = {
name: 'John',
profile: {
bio: 'Developer',
skills: ['JS', 'React', 'Node']
}
};
const copy = deepCopy(user);
copy.profile.skills[0] = 'TypeScript'; // Оригинал не меняется
3. Structured Clone API (современный стандарт)
// Поддержка: Chrome 98+, Firefox 98+, Safari 15.4+, Node 17+
const copy = structuredClone(user);
// Работает с Date, Map, Set, Blob, ArrayBuffer
const obj = {
date: new Date(),
map: new Map([['a', 1]]),
set: new Set([1, 2, 3])
};
const copy = structuredClone(obj);
console.log(copy.date instanceof Date); // true!
// НО не работает с функциями
const obj = { greet: () => 'Hello' };
const copy = structuredClone(obj); // Error: not cloneable
4. Lodash (production-ready)
import _ from 'lodash';
const copy = _.cloneDeep(user);
// Работает с функциями, обычными объектами, массивами
// Поддерживает циклические ссылки
const circular = { a: 1 };
circular.self = circular;
const copy = _.cloneDeep(circular); // Работает!
В практике React
Обновление nested state:
function UserEditor() {
const [user, setUser] = useState({
name: 'John',
profile: { bio: 'Developer' }
});
// Неправильно
const handleBioChange = (newBio) => {
user.profile.bio = newBio; // Мутация!
setUser(user); // React не заметит изменения
};
// Правильно
const handleBioChange = (newBio) => {
setUser(prev => ({
...prev,
profile: { ...prev.profile, bio: newBio }
}));
};
// Или с глубокой копией
const handleBioChange = (newBio) => {
const updated = structuredClone(user);
updated.profile.bio = newBio;
setUser(updated);
};
return (
<input
value={user.profile.bio}
onChange={(e) => handleBioChange(e.target.value)}
/>
);
}
С Immer (лучше для сложных обновлений):
import { useImmer } from 'use-immer';
function UserEditor() {
const [user, updateUser] = useImmer({
name: 'John',
profile: { bio: 'Developer' }
});
// Immer автоматически создаёт deep copy
const handleBioChange = (newBio) => {
updateUser(draft => {
draft.profile.bio = newBio; // Выглядит как мутация
});
};
return (
<input
value={user.profile.bio}
onChange={(e) => handleBioChange(e.target.value)}
/>
);
}
С Redux
// Bad pattern
const initialState = {
users: [{ id: 1, name: 'John', profile: { bio: 'Dev' } }]
};
const reducer = (state, action) => {
if (action.type === 'UPDATE_USER') {
state.users[0].profile.bio = action.payload; // Мутация!
return state;
}
};
// Good pattern
const reducer = (state, action) => {
if (action.type === 'UPDATE_USER') {
return {
...state,
users: state.users.map(u =>
u.id === action.userId
? {
...u,
profile: { ...u.profile, bio: action.payload }
}
: u
)
};
}
};
// Best pattern (с Immer)
import produce from 'immer';
const reducer = produce((draft, action) => {
if (action.type === 'UPDATE_USER') {
const user = draft.users.find(u => u.id === action.userId);
if (user) user.profile.bio = action.payload;
}
});
Производительность
// Deep copy большого объекта — дорого!
const bigData = { /* 10000 объектов */ };
// Плохо
const copy = structuredClone(bigData); // Медленно
// Хорошо
const copy = { ...bigData }; // Быстро (shallow)
// Лучше
const copy = produce(bigData, draft => {
// Изменяем только нужные поля
}); // Копирует только изменённые ветки
Когда НЕ нужна глубокая копия
// Передаём данные в дочерний компонент
const UserCard = ({ user }) => {
// user — read-only, не мутируем
return <div>{user.name}</div>;
};
// НЕ нужна копия здесь
<UserCard user={userData} /> // OK
// Нужна только если мутируем
const UserEditor = ({ user: initialUser }) => {
const [user, setUser] = useState(initialUser);
// Теперь user — наше состояние, можно мутировать
};
Мой выбор в production
// 1. Для простых объектов и массивов → spread operator
setUser({ ...user, name: newName });
// 2. Для вложенных структур → structuredClone (если поддержка браузеров OK)
const copy = structuredClone(user);
// 3. Для сложных обновлений → Immer
updateUser(draft => {
draft.profile.bio = newBio;
});
// 4. Для полной поддержки браузеров → Lodash
import _ from 'lodash';
const copy = _.cloneDeep(user);
Главное правило
В React/Redux: используй иммутабельные обновления. Глубокая копия — инструмент для этого, но не все случаи требуют глубокой копии. Обычно хватает shallow copy через spread оператор.