const original = {
num: 1000,
bool: true,
str: "test",
func: function () {
console.log("func");
},
obj: {
x: 1,
y: 2,
},
arr: ["A", "B", "C"],
};
자바스크립트에서 객체는 key와 value를 가지는데 위의 예시처럼 key값은 문자열인 것과 비교하면 값은 모든 데이터형을 가질 수 있다는 특징이 있다.
객체를 복제할 때 초보자들이 가장 많이 하는 실수는 다음과 같이 =연산자를 통해 새로운 변수에 복사할 객체를 할당하는 것이다.
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elemsntSchool : "test3"
}
}
const person2 = person1;// 참조할당
p1.age = 30;
console.log(p1.age)//30
console.log(p2.age)//30
person2에 person1을 할당할 때, person1의 값 전체가 복사되는 것이 아니라 주소값이 복사된 것이기 때문에 person1의 값이 변경이 되면 person2의 값도 함께 변경이 된다.
즉 person2라는 객체는 person1이라는 객체의 값을 복사 한 것이 아닌 person1객체의 값을 참조한다는 의미
객체를 복제할 때 다음으로 많이 하는 방법은 객체의 얕은 복사
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elementSchool : "test3"
}
};
const target = {
position = "Frontend"
};
const person2 = Object.assign(target, person1);
person2.age = 30;
console.log(person2.age) //30
console.log(target.age) //30
console.log(person1.age) //32
//person2의 age를 변경하더라도 person1의 age는 변경되지 않는다.
Object.assign(target, …sources) 메서드를 사용하면 첫번째 인자로 두번째 인자의 속성들을 복사할 수 있다. 따라서 위와같이 언뜻 보면 원본과 복사본이 서로 영향을 주지 않고 변경이 가능한 것처럼 보인다.
하지만 단순 속성이 아닌 객체나 배열 속성을 변경해보면 아래와 같이 여전히 서로 영향을 준다는 것을 알 수 있다.
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elementSchool : "test3"
}
};
const target = {
position = "Frontend"
};
const person2 = Object.assign(target, person1);
person2.school.highSchool = "seoulHighSchool";
console.log(person2.school.highSchool) //seoulHighSchool
console.log(person1.school.highSchool) //seoulHighSchool
이런 현상이 발생하는 이유는 객체를 하나의 트리구조로 봤을 때 최상위 레벨의 속성만 복사를 하는 Object.assign(target, …person1)메서드의 동작방식에 있다. 객체트리의 최말단 노드까지 복사되지 않기 때문에 이러한 복제방식을 얕은복제(shallow clone)라고 일컫는다.
물론 의도적으로 깊은 복제 대신에 얕은 복제를 하는 경우도 있을 수 있다. 얕은복제는 아래와 같이 …(Spread Operator)를 활용해서 더 간결하게 처리 할 수 있다.
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elementSchool : "test3"
}
};
const person2 = {...person1}
person2.name = "Kim"
person2.school.highSchool = "seoulHighSchool";
console.log(person1.name)//kim
console.log(person2.name)//dong
console.log(person2.school.highSchool) //seoulHighSchool
console.log(person1.school.highSchool) //seoulHighSchool
객체 트리의 최말단 노드까지 복제하고 싶을때는 어떻게 처리해야될까?
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elementSchool : "test3"
},
test: () => {
console.log("function test")
}
};
const person2 = JSON.parse(JSON.stringify(person1));
person2.age = 30;
person2.school.highSchool = "seoulHighSchool"
console.log(person1.age) //32
console.log(person2.age) //30
//객체트리의 최말단 영역까지 copy가능
console.log(person1.school.highSchool) //test
console.log(person2.school.highSchool) //seoulHighSchool
위와 같이 JSON객체의 메소드를 이용하여 deep copy를 할 수 있다.
→ JSON.stringify로 객체트리의 최말단영역까지 문자열로 변환 후 JSON.parse로 자바스크립트 객체로 변환
JSON문자열로 변환하고 다시 객체로 변환해 주기 때문에 객체에 대한 참조가 없어진다.
deep clone 방법으로는 직접 깊은 복사(deep clone)를 실현하는 함수를 짜는것이다.
복사를 진행하다가 객체를 만나면 함수를 재귀적으로 실행해 깊은 복사(deep clone)를 실현 하는 것인데
function cloneObject(obj) {
var clone = {};
for (var key in obj) {
if (typeof obj[key] == "object" && obj[key] != null) {
clone[key] = cloneObject(obj[key]);
} else {
clone[key] = obj[key];
}
}
return clone;
}
객체의 프로퍼티를 순환하며 빈 오브젝트에 더하고, 프로퍼티가 오브젝트일 경우 재귀적으로 함수를 실행하며 복사를 진행한다.
마지막으로 가장 손쉽게 Lodash를 사용해 깊은복사하는 방법이 있다 이미 코드내에서 기존에 Lodash를 사용하고 있었다면 Lodash를 활용해 깊은 복사를 진행하는것도 좋은 선택지이다.
const person1 = {
name:"dong",
age : 32,
school : {
highSchool : "test",
middleSchool : "test2",
elementSchool : "test3"
},
test: () => {
console.log("function test")
}
};
let person2 = _.cloneDeep(person1);
person2.school.highSchool = "seoulHighSchool";
console.log(person1.school.highSchool); //test
console.log(person2.school.highSchool); //seoulHighSchool