Не попадайтесь на этот каверзный вопрос интервью!

В приведенном ниже примере объект объявлен как переменная x. Затем переменной y присваивается значение x. Значение y.a, которое изначально было 1, затем переназначается как 2. Каков будет результат console.log(x.a)?

let x = {a:1, b:2, c:{nestedA:1, nestedB:2}, d:4}
let y = x
y.a = 2
console.log(x.a)

Если ваш ответ был 2, вы правы. Причина этого в том, что y не является копией x: y напрямую ссылается на тот же объект, на который ссылается x. Таким образом, если y изменяется или «мутирует», значение x также изменяется. Как же тогда нам создать копию, чтобы оригинал не изменился?

Мы могли бы использовать оператор распространения {…} или Object.assign() для создания поверхностной копии. Теперь, когда у нас есть оператор распространения во 2-й строке примера ниже, каков будет результат console.log(x.a)?

let x = {a:1, b:2, c:{nestedA:1, nestedB:2}, d:4}
let y = {...x}
y.a = 2
console.log(x.a)

Console.log(x.a) в приведенном выше примере вернет 1. Это связано с тем, что исходный x не изменяется при переназначении y.a. Теперь здесь все становится сложнее. Давайте посмотрим на другой пример, но на этот раз с вложенными значениями. Что будет результатом журнала консоли?

let x = {a:1, b:2, c:{nestedA:1, nestedB:2}, d:4}
let y = {...x}
y.c.nestedB = 4
console.log(x.c.nestedB)

Результатом будет 4. Это потому, что оператор распространения просто делает поверхностную копию, а не глубокую копию x. Вложенные значения внутри неглубокой копии по-прежнему указывают на оригинал.

Поверхностные копии используют меньше памяти, чем глубокая копия, поэтому они работают быстрее, если вы не используете вложенные значения. Но если мы действительно хотим сделать копию, которая занимает свое место в памяти и не указывает на оригинал, мы должны сделать глубокую копию.

Обычный способ сделать глубокую копию в javascript — использовать lodash, внешнюю библиотеку. Вот инструкции по созданию глубокого клона с помощью lodash. Однако для быстрого и грязного обходного пути мы можем сделать глубокую копию, преобразовав данные в JSON и проанализировав их обратно. Вот как это будет выглядеть:

let x = {a:1, b:2, c:{nestedA:1, nestedB:2}, d:4}
let y = JSON.parse(JSON.stringify(x))
y.c.nestedB = 4
console.log(x.c.nestedB)

Если вы еще этого не сделали, проверьте это в Node.js или на консоли Chrome. Результатом console.log будет 2, потому что глубокая копия полностью отделена от оригинала. Я надеюсь, что вы нашли этот пост поучительным, спасибо за чтение!