← Назад к вопросам

Как сделать два разных объекта из одного?

2.0 Middle🔥 161 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как сделать два разных объекта из одного

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

1. Поверхностное копирование (Shallow Copy)

Поверхностная копия копирует только первый уровень свойств. Вложенные объекты остаются ссылками на оригинальные:

// Объект для примеров
const original = {
  name: "John",
  profile: {
    age: 30,
    city: "Moscow"
  }
};

// Способ 1: Spread оператор
const copy1 = { ...original };
copy1.name = "Jane";
console.log(original.name); // "John" - изменилось не исходное имя

copy1.profile.age = 25;
console.log(original.profile.age); // 25 - ИЗМЕНИЛОСЬ! Вложенный объект - это ссылка

// Способ 2: Object.assign()
const copy2 = Object.assign({}, original);
copy2.name = "Jane";
console.log(original.name); // "John"

// Способ 3: for...in цикл
const copy3 = {};
for (const key in original) {
  copy3[key] = original[key];
}
copy3.name = "Jane";
console.log(original.name); // "John"

Поверхностная копия достаточна для простых объектов, но не для вложенных структур.

2. Глубокое копирование (Deep Copy)

Глубокая копия рекурсивно копирует ВСЕ уровни объекта:

Способ 1: JSON (простой, но с ограничениями)

const original = {
  name: "John",
  profile: {
    age: 30,
    city: "Moscow"
  },
  hobbies: ["reading", "coding"],
  updated: new Date()
};

// JSON способ - самый быстрый
const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.name = "Jane";
deepCopy.profile.age = 25;
deepCopy.hobbies.push("gaming");

console.log(original.name); // "John"
console.log(original.profile.age); // 30
console.log(original.hobbies); // ["reading", "coding"]

Минусы JSON подхода:

  • Не копирует функции
  • Не копирует undefined
  • Теряет Date объекты (становятся строками)
  • Не копирует Symbol и другие специальные типы

Способ 2: Рекурсивная функция (более гибкий)

function deepCopy(obj) {
  // Обработка примитивных типов
  if (obj === null || typeof obj !== "object") {
    return obj;
  }
  
  // Обработка Date
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
  
  // Обработка Array
  if (obj instanceof Array) {
    return obj.map(item => deepCopy(item));
  }
  
  // Обработка Object
  if (obj instanceof Object) {
    const copy = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        copy[key] = deepCopy(obj[key]);
      }
    }
    return copy;
  }
  
  return obj;
}

// Использование
const original = {
  name: "John",
  profile: {
    age: 30,
    updated: new Date("2024-01-01")
  },
  hobbies: ["reading", "coding"]
};

const copy = deepCopy(original);
copy.profile.updated.setFullYear(2025);
console.log(original.profile.updated.getFullYear()); // 2024 - исходный не изменился

Способ 3: structuredClone() (современный API)

// Встроенный способ в современных браузерах
const original = {
  name: "John",
  profile: { age: 30, updated: new Date() },
  hobbies: ["reading", "coding"]
};

const copy = structuredClone(original);

copy.profile.age = 25;
copy.hobbies.push("gaming");

console.log(original.profile.age); // 30
console.log(original.hobbies); // ["reading", "coding"]

// structuredClone поддерживает Date, Map, Set, ArrayBuffer и другие типы
// Не копирует функции (выбросит ошибку)

3. Копирование с пользовательской логикой

Иногда нужна частичная копия или трансформация:

// Копирование с фильтрацией полей
function copyWithFilter(obj, allowedKeys) {
  const copy = {};
  allowedKeys.forEach(key => {
    if (key in obj) {
      copy[key] = obj[key];
    }
  });
  return copy;
}

const user = {
  id: 1,
  name: "John",
  email: "john@example.com",
  password: "secret"
};

// Копируем без пароля
const safeCopy = copyWithFilter(user, ["id", "name", "email"]);
console.log(safeCopy); // { id: 1, name: "John", email: "john@example.com" }

// Копирование с трансформацией
function copyWithTransform(obj, transform) {
  const copy = { ...obj };
  return transform(copy);
}

const transformed = copyWithTransform(user, (copy) => {
  copy.name = copy.name.toUpperCase();
  return copy;
});
console.log(transformed.name); // "JOHN"

4. Копирование объектов в React

В React часто используют спред оператор для создания новых состояний:

// Текущее состояние
const [user, setUser] = useState({
  name: "John",
  profile: { age: 30, city: "Moscow" }
});

// Неправильно - изменяет вложенный объект
const handleIncreaseAge = () => {
  user.profile.age += 1;
  setUser(user); // React не заметит изменение!
};

// Правильно - спред оператор для первого уровня
const handleIncreaseAge = () => {
  setUser({
    ...user,
    profile: {
      ...user.profile,
      age: user.profile.age + 1
    }
  });
};

// Или с structuredClone (в новых приложениях)
const handleIncreaseAge = () => {
  const newUser = structuredClone(user);
  newUser.profile.age += 1;
  setUser(newUser);
};

5. Копирование массивов объектов

const questions = [
  { id: 1, title: "Q1", answers: [{ text: "A1" }] },
  { id: 2, title: "Q2", answers: [{ text: "A2" }] }
];

// Поверхностная копия массива (но объекты внутри - ссылки)
const shallowCopy = [...questions];
shallowCopy[0].title = "New Q1";
console.log(questions[0].title); // "New Q1" - ИЗМЕНИЛОСЬ!

// Глубокая копия массива
const deepCopy = questions.map(q => structuredClone(q));
deepCopy[0].title = "New Q1";
console.log(questions[0].title); // "Q1" - не изменилось

// Или с полной рекурсией
const deepCopy2 = JSON.parse(JSON.stringify(questions));

6. Сравнение производительности

const largeObj = {
  level1: { level2: { level3: { level4: { data: [1, 2, 3] } } } }
};

// JSON - БЫСТРО для простых объектов
console.time("JSON");
const copy1 = JSON.parse(JSON.stringify(largeObj));
console.timeEnd("JSON"); // ~0.1ms

// structuredClone - СОВРЕМЕННО и надежно
console.time("structuredClone");
const copy2 = structuredClone(largeObj);
console.timeEnd("structuredClone"); // ~0.2ms

// Рекурсивная функция - ГИБКО, но медленнее
console.time("recursive");
const copy3 = deepCopy(largeObj);
console.timeEnd("recursive"); // ~0.5ms

Когда использовать какой метод

  • Spread { ...obj } - для простых объектов, первый уровень
  • JSON методы - для быстрого копирования примитивов и простых структур
  • structuredClone() - для современных приложений, полная поддержка типов
  • Рекурсивная функция - для кастомной логики и трансформации
  • Библиотеки (lodash.cloneDeep) - для критичного функционала

Выбор метода зависит от структуры данных, поддерживаемых браузеров и требуемой функциональности.

Как сделать два разных объекта из одного? | PrepBro