깊은 복사 vs 얕은 복사
우선 자바스크립트의 자주 쓰이는 타입들을 살펴보자. 복사의 차원에선 자바스크립트의 타입을 참조형
과 기본형
으로 나눌 수 있다. 값과 주소로 이루어진 참조형과 값 자체인 원시형 기본형 타입으로 나누어진다.
참조형 (Reference type) : 값 - 주소로 이루어짐.
- object (array 포함)
- function
기본형 (Primitive type) : 값 자체
- number
- string
- boolean (true/false)
- null / undefined
- symbol
얕은 복사(Shallow Copy)
얕은 복사는 '주소'만 복사한다. 기본 타입은 주소가 없기 때문에 그 값을 복사한다. 문제는 참조형이 주소만 복사하기 때문에 원본이 변하면, 복사된 값도 변화한다는 것이다. 반대의 경우도 마찬가지로 적용된다.
let num = 1;
let arr = [1];
let newNum = num;
let newArr = [...arr];
newNum = 2,
newArr[0] = 2;
console.log(num, newNum); //1, 2
console.log(arr, newArr); // [2], [2]
깊은 복사(Deep Copy)
깊은 복사는 '값-주소'를 동시에 복사한다. 방법은 Array.slice()
, Object.assign({}, 복사하고자하는 객체)
를 이용하면 된다. 원본 또는 복사본이 변화해도 서로에게 영향을 미치지 않는다.
let arr = [1];
let arrS = [...arr]; // 얕은 복사
let arrD = arr.slice(); // 깊은 복사
let arrD1 = Object.assign([], arr); // 깊은 복사
let arr[0] = 2;
console.log(arr, arrS, arrD, arrD1)
// [2], [2], [1], [1]
이 역시 문제가 있다. 깊은 복사는 그 복사를 한 깊이에서만 깊은 복사를 한다는 것이다. 위의 두가지 매소드는 원소 안에까지 깊이 돌아가며 깊은 복사를 해주지 않는다는 점이다.
let arr = [1 , 2, ['a']];
let newArr = arr.slice();
newArr[1] = 3;
newArr[2][0] = 'b';
console.log(arr); //[1, 2, ['b']]
console.log(newArr); //[1, 3, ['b']]
상단의 예제를 보면 2 depth로 구성된 배열의 경우, 2 깊이의 배열은 깊은 복사가 되지 않고, 여전히 원본을 참조하고 있다. 따라서 사본의 값이 변화할 때 원본의 값도 변화했다.
이를 방지하기 위해 JSON.stringify()
를 이용할 수 있다. JSON으로 해당 참조형을 통제로 변환한 뒤에 이를 다시 JSON.parse()
해주는 것이다. 혹은 Lodash
나 Ramada
패키징이 모든 깊이의 깊은 복사를 구현해놓았다. 자세한 설명은 다음의 미디엄을 참고할 것
깊이를 파고 들어가면서, 참조형 타입이 있으면 깊은 복사로 그 때 마다 복사해주면 되지 않을까? 2 깊이의 배열을 가지고 여러가지 실험을 해보았다.
map()
, forEach()
를 이용해서 원소의 참조형도 깊은 복사를 해보자.
let arr = [[1], [2]];
let newArr = arr.slice();
newArr.map(el => { //forEach도 같은 결과
let copy = el.slice();
copy.push(1); //push 는 mutable 하다.
console.log(copy) // [1,1] [2,1] 잘 출력한다.
return copy;
});
console.log(arr, newArr)
// 그러나 결과는[[1],[2]], [[1],[2]]
for...of
를 이용해본다.
let arr = [[1], [2]];
let newArr = arr.slice();
for(let el of newArr) {
let copy = el.slice();
copy.push(1); //push 는 mutable 하다.
console.log(copy) // [1,1] [2,1] 잘 출력한다.
el = copy.slice();
// 이부분이 잘못인가..? el = copy해도 결과는 다르지 않았다.
}
console.log(arr, newArr)
// 그러나 결과는[[1],[2]], [[1],[2]]
for
문을 이용해본다.
let arr = [[1], [2]];
let newArr = arr.slice();
for(let i = 0; i < arr.length; i++) {
let copy = newArr[i].slice();
copy.push(1); //push 는 mutable 하다.
console.log(copy) // [1,1] [2,1] 잘 출력한다.
newArr[i] = copy
}
console.log(arr, newArr)
// 드디어 [[1],[2]], [[1,1],[2,1]]
왜 위의 2가지는 안되고, 아래의 for문은 가능한지 아직도 의문이다. 혹시나 이 포스트를 발견하신 분... 댓글 좀 달아주세요...🙌
'2. 우당탕탕 개발자 > 2-2. 상세 노트' 카테고리의 다른 글
Docker를 이용해서 React.js를 배포하기 1 (0) | 2021.07.18 |
---|---|
Docker volume & compose (0) | 2021.07.15 |
AWS Amplify로 Git 최신 merge 내용으로 자동 배포하기 (0) | 2021.07.13 |
Docker란 무엇인가? (4) | 2021.07.01 |
[CSS] px | em | rem 차이를 알아보자 (0) | 2021.06.28 |
댓글