Привет народ👋 надеюсь у вас все хорошо.
Вы, наверное, думаете: «Клонировать объект»? Что в этом такого?
Ну, я тоже так думал, пока не столкнулся с проблемой, которая заняла у меня 2 дня, чтобы отладить, что я сделал что-то ужасное с клонированием объекта.
Итак, давайте посмотрим, как мы можем клонировать объекты в javascript.
// we have a user object
const user = {
name:"Deepak Negi",
email:"st.deepak15@gmail.com"
}
Теперь, если мы хотим скопировать этот пользовательский объект, все просто!
const copiedUser = user;
Легко, да?… ну это худший способ копирования пользователя, очевидно, что у вас есть некоторые неправильные представления о том, что делает оператор const copiedUser = user;
.
В JavaScript объекты передаются и присваиваются по ссылке (точнее, по значению ссылки), поэтому user
и copiedUser
— это ссылки на один и тот же объект.
// [Object1]<--------- user
const copiedUser = user;
// [Object1]<--------- user
// ^
// |
// ----------- copiedUser
Как видно после присваивания, обе ссылки указывают на один и тот же объект.
const user = {
name:"Deepak Negi",
email:"st.deepak15@gmail.com"
}
const copiedUser = user;
copiedUser.name = "XYZ"
console.log(copiedUser) // {name:"XYZ",email:"st.deepak15@gmail.com"}
console.log(user) // {name:"XYZ",email:"st.deepak15@gmail.com"}
Изменение любой из них приведет к изменению обеих 🙁
Как же тогда создать копию, если нам нужно изменить один объект, а не другой?
1. Оператор разворота
const spreadUser = {...user}
spreadUser.name = "XYZ"
console.log(spreadUser) // {name:"XYZ",email:"st.deepak15@gmail.com"}
console.log(user) // {name:"Deepak Negi",email:"st.deepak15@gmail.com"}
2. Object.assign()
const assignUser = Object.assign({}, user);
assignUser.name = "XYZ"
console.log(assignUser) // {name:"XYZ",email:"st.deepak15@gmail.com"}
console.log(user) // {name:"Deepak Negi",email:"st.deepak15@gmail.com"}
Наконец-то мы разобрались!
Если вы думаете, что это все… то нет… есть еще много интересного, теперь мы добавили еще несколько данных в объект пользователя и посмотрим, что произойдет.
const user = {
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"New Delhi",
state:"Delhi",
zipcode: 000000,
country:"India"
}
}
const spreadUser = {...user}
spreadUser.address.city = "Pune"
spreadUser.address.state = "Mumbai"
console.log(spreadUser)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"Pune",
state:"Mumbai",
zipcode: 000000,
country:"India"
}
}
console.log(user)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"Pune",
state:"Mumbai",
zipcode: 000000,
country:"India"
}
}
Вы видите проблему, наш фактический объект user теперь тоже изменился, и это происходит с методом Object.assign()
.
Но почему?
Из-за неглубокого копирования, т.е. оператор object spread, как и Object.assign, не клонирует значения вложенных объектов, а копирует ссылку на вложенный объект. Это называется неглубоким копированием.
Что же нам тогда делать? Глубокое копирование?
Да, Deep copy/Deep clone будет копировать объект, даже вложенные свойства, для этого сериализуйте объект в JSON и разберите его обратно в JS объект.
const user = {
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"New Delhi",
state:"Delhi",
zipcode: 000000,
country:"India"
}
}
const deepCopiedUser = JSON.parse(JSON.stringify(user))
deepCopiedUser.address.city = "Pune"
deepCopiedUser.address.state = "Mumbai"
console.log(deepCopiedUser)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"Pune",
state:"Mumbai",
zipcode: 000000,
country:"India"
}
}
console.log(user)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"New Delhi",
state:"Delhi",
zipcode: 000000,
country:"India"
}
}
Итак, теперь наш оригинальный объект user
не изменяется, когда мы изменяем deepCopiedUser
.
Остерегайтесь использовать
JSON.parse(JSON.stringify(user))
, если вы имеете дело с датой, функциями, RegExps, Maps, Sets или другими сложными типами внутри вашего объекта. Метод JSON не может работать с такими типами.
Поэтому для таких случаев, вероятно, лучше всего подойдет метод lodash clonedeep.
import {cloneDeep} from 'lodash'
or
const {cloneDeep} = require('lodash')
const user = {
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"New Delhi",
state:"Delhi",
zipcode: 000000,
country:"India"
}
}
const deepCloneUser = cloneDeep(user)
deepCloneUser.address.city = "Pune"
deepCloneUser.address.state = "Mumbai"
console.log(deepCloneUser)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"Pune",
state:"Mumbai",
zipcode: 000000,
country:"India"
}
}
console.log(user)
// console output
{
name:"Deepak Negi",
email:"st.deepak15@gmail.com",
address:{
line1:"ABC, Tower X",
city:"New Delhi",
state:"Delhi",
zipcode: 000000,
country:"India"
}
}
Наконец-то!!!
Дайте мне знать в комментариях, какой, по вашему мнению, лучший способ глубокого клонирования объекта.