객체의 복사, 불변성의 문제
1. issue
실무에서 다음 a와 같은 구조의 배열을 복사해서 안의 프로퍼티 값을 변경하니 a의 불변성이 지켜지지 않는 문제가 발생했다.
const a = [
{ first: 1, second: 2, third: 3 },
{ first: 4, second: 5, third: 6 },
{ first: 7, second: 8, third: 9 },
];
1) 전개구문(spread syntax)
배열 a를 전개 구문으로 복사한 b를 map을 이용해 안의 객체 first의 값을 바꿔보자.
그러면 a의 값 또한 변하면서 불변성이 지켜지지 않는다.
let b = [...a];
b.map((obj) => {
obj.first = 10;
});
// console.log("a", a);
// console.log("b", b);
// a [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
// b [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
전개구문은 다음과 같은 특성이 있기 때문이다.
Spread 문법은 배열을 복사할 때 1 레벨 깊이에서 효과적으로 동작합니다. 그러므로, 다음 예제와 같이 다차원 배열을 복사하는것에는 적합하지 않을 수 있습니다.
const arr = [1, 2, 3]; const arr2 = [...arr]; // arr.slice() 와 유사 arr2.push(4); // console.log("arr", arr); // console.log("arr2", arr2); // arr [ 1, 2, 3 ] // arr2 [ 1, 2, 3, 4 ] const arr3 = [[1], [2], [3]]; const arr4 = [...arr3]; arr4.shift().shift(); // console.log("arr3", arr3); // console.log("arr4", arr4); // arr3 [ [], [ 2 ], [ 3 ] ] // arr4 [ [ 2 ], [ 3 ] ]
2) Array.prototype.slice()
let c = a.slice();
c.map((obj) => {
obj.first = 10;
});
// console.log("a", a);
// console.log("c", c);
// a [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
// c [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
Array.prototype.slice()로 객체를 참조하는 경우 다음과 같은 특성이 있다.
slice()는 객체 참조를 새 배열로 복사합니다. 원본 배열과 새 배열은 모두 동일한 객체를 참조합니다. 참조 된 객체가 변경되면 변경 내용은 새 배열과 원래 배열 모두에서 볼 수 있습니다.
3) Array.prototype.slice.call()
call()은 이미 할당되어있는 다른 객체의 함수/메소드(아래 예시에서는 slice)를 호출하는 해당 객체(아래 예시에서는 a)에 재할당할 때 사용한다.
let d = Array.prototype.slice.call(a);
d.map((obj) => {
obj.first = 10;
});
// console.log("a", a);
// console.log("d", d);
// a [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
// d [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
4) Array.prototype.concat()
let e = [].concat(a);
e.map((obj) => {
obj.first = 10;
});
console.log("a", a);
console.log("e", e);
// a [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
// e [
// { first: 10, second: 2, third: 3 },
// { first: 10, second: 5, third: 6 },
// { first: 10, second: 8, third: 9 }
// ]
2. solution
위와 같은 상황이 발생하는 이유는 우선 객체는 복사할 때 일반 문자열, 숫자, 불린 같은 경우와 다르게 다른 변수에 대입할 때 값을 복사하는 게 아니라 메모리의 주소를 복사하기 때문이다. 이를 다른 말로 참조라고 한다.
shallow copy는 가장 상위 객체만 새로 생성되고 내부 객체들은 참조 관계인 경우를 의미하고, deep copy는 내부 객체까지 모두 새로 생성된 것을 의미한다.
위의 1), 2), 3), 4) 모두 얕은 복사를 했기때문에 a 배열 안 객체의 불변성이 지켜지지 못한 것이다.
객체를 깊은 복사하기 위해서는 다음과 같이 hasOwnProperty로 부모가 아닌 본인이 가진 프로퍼티일 때 copyObj(obj[attr])와 같이 재귀적으로 일반 문자열, 숫자, 불린 값이 될 때까지 값을 불러온 뒤 최종적으로 copy = obj를 통해 복사한다.
function copyObj(obj) {
let copy = {};
if (Array.isArray(obj)) {
copy = obj.slice().map((v) => {
return copyObj(v);
});
} else if (typeof obj === "object" && obj !== null) {
for (let attr in obj) {
if (obj.hasOwnProperty(attr)) {
console.log("attr", attr);
console.log("obj[attr]", obj[attr]);
// attr a
// obj[attr] 1
// attr b
// obj[attr] 2
// attr c
// obj[attr] [ { d: null, e: 'f' } ]
// attr d
// obj[attr] null
// attr e
// obj[attr] f
copy[attr] = copyObj(obj[attr]);
}
}
} else {
console.log("obj", obj);
// obj 1
// obj 2
// obj null
// obj f
copy = obj;
}
return copy;
}
const obj = { a: 1, b: 2, c: [{ d: null, e: "f" }] };
const obj2 = copyObj(obj);
obj2.a = 3;
obj2.c[0].d = true;
console.log(obj.a); // 1
console.log(obj.c[0].d); // null
출처:
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
https://www.zerocho.com/category/JavaScript/post/5750d384b73ae5152792188d
Author And Source
이 문제에 관하여(객체의 복사, 불변성의 문제), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@maliethy/객체의-복사-불변성의-문제저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)