자바스크립트에서 객체가 들어있는 어떤 변수에 객체를 대입하면 두 변수는 참조 관계가 이루어진다.
let obj = { a: 1, b: 2, c: 3 }
let obj2 = { a: 1, b: 2, c: 3 };
console.log(obj === obj2) // false
우선 기본적으로 obj 와 obj2 는 각각 들어있는 객체의 데이터가 같아도 두 배열을 비교 연산시 false 값을 출력한다.
let obj2;
let obj = { a: 1, b: 2, c: 3 };
let obj2 = obj // 참조 관계가 이루어짐.
console.log(obj === obj2) // true
obj2.a = 10;
console.log(obj); // { a: 10, b: 2, c: 3 )
그런데 obj2의 값에 obj의 값을 대입하면 두 변수는 참조 관계가 이루어진다.
참조 관계에서 어느 하나의 객체 내 속성(key)값을 변경하면 참조 관계에 있는 다른 객체의 속성 값도 변화한다.
* 참조 관계가 이루어지지 않은 상태에서는 obj의 속성 값이 변화하지 않는다.
let obj = { a: 1, b: 2, c: 3 }
Object.keys(obj) // ["a", "b", "c"]
obj2 = {};
Object.keys(obj).forEach((key) => {
obj2[key] = obj[key]
)};
console.log(obj) // { a: 1, b: 2, c: 3 }
obj2.a = 10;
console.log(obj.a) // 1
결국 어떤 객체를 다른 객체에 대입을 하는 과정에서 두 객체가 참조 관계가 되기 때문에 이 문제를 위와 같이 해결할 수 있다. Object.keys(obj) 메서드는 obj에 들어있는 속성(key)값 만을 가지고 새 객체를 만든다.
값이 들어있지 않은 obj2의 빈 배열안에 obj의 값들을 복사하고자 할 때 obj2 안에 obj의 key값을 넣어주는 방식이다.
이렇게 하면 obj2의 속성값을 변경해도 obj의 속성값은 변화하지 않는다.
let obj = { a: 1, b: { c: 1 } };
let obj2 = {};
Object.keys(obj).forEach((key) => {
obj2[key] = obj[key]
})
console.log(obj2) // { a: 1, b: { c: 1 } }
obj2.a = 10;
console.log(obj.a) // 1
obj2.b.c = 20
console.log(obj.b.c) // 20
하지만 이러한 방식은 문제가 있다. 객체 안에 또 다른 객체가 들어있는 형태를 가진 변수의 경우 위와 같은 Object.keys() 메서드를 이용하면 복사는 가능하지만 obj2의 배열 안에 들어있는 객체의 값을 변경하면 obj의 객체안의 객체의 값이 바뀌어버린다.
즉, obj의 a는 Object.keys(obj)의 원시 key값에 해당하여 복사가 가능했지만 b의 경우는 그 값이 객체 자체이기 때문에 obj의 b와 obj2의 b가 서로 참조 관계가 된 것이다.
obj = { a: 1, b: { c: 3 } };
obj2 = JSON.parse(JSON.stringify(obj));
obj2.b.c = 20;
console.log(obj.b.c); // 3
이 문제는 위와 같이 입력하여 해결할 수 있다. 다만, 성능이 최악으로 작용하며, 깊은 복사를 위한 완전한 방법은 아니다.
arr = [1, 2, 3, 4, 5]
arr2 = arr.slice()
console.log(arr) // [1, 2, 3, 4 ,5]
// slice()
arr = [1, 2, 3, 4, 5]
arr2 = arr.slice(3) // 3번 index부터 끝까지 복사 : [4, 5]
arr2 = arr.slice(1, -2) // 1번 index부터 -2번 index 직전까지 복사 : [2, 3]
간단한 배열 끼리의 참조 문제를 해결하기 위한 경우 slice() 를 활용할 수도 있다.
위 코드에서 slice() 는 arr의 배열 전체를 복사하여 arr2의 값으로 넣는다.
참고로 "참조" 는 다른 말로 얕은 복사, "복사" 는 다른 말로 깊은 복사라고도 한다.
let obj1 = {a: 1, b: 2, c: 3}
let obj2 = Object.assign({}, obj1)
console.log(obj2) // {a: 1, b: 2, c: 3}
위 방식도 깊은 복사에 속하지만 비완전하다.