Давайте вместе рассмотрим ответ на этот весьма специфичный для JavaScript вопрос. С ним часто можно столкнуться на собеседованиях на позицию Frontend-разработчика. Статья будет особенно полезна, если вы готовитесь к собеседованию или начинаете глубже погружаться в JS.
Многие из нас, кто участвовал в разработке особенно крупных проектов, наверняка сталкивались с библиотеками JavaScript, такими как Lodash.js, Underscore.js. Упомянутые библиотеки предоставляют вспомогательные функции для вещей, которые не встроены в JavaScript. Одна из этих функций — глубокое копирование объектов в JavaScript. Многие из нас знают, как копировать объекты, у которых есть только один уровень вложенности. Как быть если объект содержит несколько уровней вложенности? В JavaScript нет встроенного способа скопировать этот объект так, чтобы созданная копия была полной копией оригинального объекта.
Многим из вас может быть интересно, почему задают этот вопрос? И какой в нем кроется подвох? Если у нас есть вспомогательные библиотеки, почему бы спокойно их не использовать? И вы абсолютно правы. Нам действительно необходимо их использовать. Но написание такой базовой функции станет проверкой на то как вы способны применять ваши фундаментальные знания, которые у вас должны присутствовать.
Перейдем к режиму решения задачи 👨💻⚔️ Но сначала давайте разберемся с правильностью ее постановки.
Итак, что имеется ввиду: Создайте полную копию объекта?
Ответ: это глубокая копия, копия которая продублирует каждый объект на пути копирования.
Вот пример использования функции, которая принимает объект в качестве аргумента и возвращает полную копию этого объекта.
// Signature function copyObject(source) { } // Usage const source = { a: 10, b: 20, c: { d: 30 } } const target = copyObject(source);
Прежде чем еще больше углубиться в вопрос, я настоятельно рекомендую вам не искать готовых решений в интернете, а попробовать решить данную задачу самостоятельно.
Несколько советов:
- Забудьте о вложенности. Попробуйте сначала скопировать каждый ключ и значение.
- Затем подумайте, как определить, является ли значение самим объектом, и что с ним делать.
И последний, самый главный совет: декомпозиция — разбивайте задачу посложнее и объемнее на мелкие подзадачи. Это совсем несложно сделать, если внимательно прочитать описание задачи.
Само решение:
Начнем с того, что просто вернем новый объект. Вот так:
function copyObject(source) { var target = {}; return target; }
Далее задача требует от нас получить глубокую копию объекта. Но прежде чем напрямую перейти к глубокому копированию, давайте сначала напишем простое решение для копирования каждого значения ключа для одного уровня вложенности.
Что нам для этого потребуется?
- Нам нужно получить все ключи исходного объекта.
- Нам нужно скопировать все эти ключи в конечный объект.
function copyObject(source) { var target = {}; const keys = Object.keys(source); keys.forEach(key => { target[key] = source[key]; }); return target; }
Чудесно! Мы решили проблему для упрощенного варианта с ключами.
Теперь пришло время подумать о вложенности. С помощью оператора typeof мы можем узнать является ли значение, соответствующее текущему ключу, объектом. И когда мы знаем, что текущее значение является объектом, мы можем создать его копию методом, известным как рекурсия (узнать больше о рекурсии здесь).
Решение задачи будет выглядеть примерно так:
function copyObject(source) { var target = {}; // Getting source object keys const keys = Object.keys(source); keys.forEach(key => { // Checking if current value is an object if (typeof source[key] === "object") { // Calling our function recursively for current value target[key] = copyObject(source[key]); } else { // Directly assigning the value target[key] = source[key]; } }); return target; }
Ура! Сейчас наше решение уже похоже на рабочее решение. Однако, в нем все еще присутствуют незначительные погрешности, такие, как обработка значений массивов и функций в объектах. Напишите код, который позволяет копировать массивы и функции и опубликуйте его в комментариях.