-
[JavaScript] 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy)Front-end 2022. 12. 8. 19:43
목적: 얕은 복사와 깊은 복사의 정의와 차이점을 알고 사용방법 익히기
얕은 복사
얕은 복사란 객체를 복사할 때 기존 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말합니다.
객체 안에 객체가 있을 경우 한 개의 객체라도 기존 변수의 객체를 잠조하고 있다면 이를 얕은 복사라고 합니다.깊은 복사
깊은 복사된 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말합니다.
원시값을 복사하는 경우
원시값을 복사하면 그 값은 다른 독립적인 메모리에 할당되기 때문에 복사한 값을 수정해도 기존 값에 영향을 주지 않는다.
참조값을 복사하는 경우
참조값을 복사하면 메모리 공간의 참조(주소 값)를 저장하기 때문에, 객체를 수정하면 기존 객체에도 영향을 미친다.
얕은 복사 방법 - slice, assign, spread
1. Array.prototype.slice()
start 부터 end 까지 (end 미포함)에 대한 얕은 복사본을 새로운 배열 객체로 반환합니다. 원본 배열은 바뀌지 않습니다. 만약 start와 end를 설정하지 않는다면, 기존 배열 전체 얕은 복사합니다.
// 1차원 배열 const array1 = [1, 2, 'a', 'b']; const copyArray1 = array1.slice(); console.log(copyArray1); // [ 1, 2, 'a', 'b' ] copyArray1[0] = 100; console.log(array1); // [ 1, 2, 'a', 'b' ] console.log(copyArray1); // [ 100, 2, 'a', 'b' ] // 2차원 배열 const array2 = [[0, 1, 2, 3, 4, 5], 1, 2, 'a', 'b']; const copyArray2 = array2.slice(); console.log(copyArray2); // [ [ 0, 1, 2, 3, 4, 5 ], 1, 2, 'a', 'b' ] copyArray2[0][0] = 100; console.log(array2); // [ [ 100, 1, 2, 3, 4, 5 ], 1, 2, 'a', 'b' ] console.log(copyArray2); // [ [ 100, 1, 2, 3, 4, 5 ], 1, 2, 'a', 'b' ]
1차원 배열일 때는 원본에 영향을 끼치지 않았지만 2차원 배열에서는 복사한 값을 변경하면 기존 값에 영향을 미치기 때문에 얕은 복사입니다.
2. Object.assign()
객체들의 모든 열거 가능한 자체 속성을 복사해 대상 객체에 붙여넣은 후 대상 객체를 반환합니다.
// 객체 const target1 = { a: 1, b: 2, }; const copyTarget1 = Object.assign({}, target1); console.log(copyTarget1); // { a: 1, b: 2 } // 중첩 객체 const target2 = { a: 1, b: 2, c: { name: 'copy', }, }; const copyTarget2 = Object.assign({}, target2); console.log(copyTarget2); // { a: 1, b: 2, c: { name: 'copy' } } copyTarget2.c.name = 'new copy'; console.log(target2); // { a: 1, b: 2, c: { name: 'new copy' } } console.log(copyTarget2); // { a: 1, b: 2, c: { name: 'new copy' } }
Object.assign()은 속성의 값을 복사하기 때문에, 깊은 복사를 수행하려면 다른 방법을 사용해야 합니다.
3. 전개연산자 (spread operator)
전개 연산자를 사용하면 배열, 문자열, 객체 등 반복 가능한 객체(Iterable Object)를 펼칠 수 있습니다.
// 객체 const target1 = { a: 1, b: 2, }; const copyTarget1 = {...target1}; console.log(copyTarget1); // { a: 1, b: 2 } // 중첩 객체 const target2 = { a: 1, b: 2, c: { name: 'copy', }, }; const copyTarget2 = {...target2}; console.log(copyTarget2); // { a: 1, b: 2, c: { name: 'copy' } } copyTarget2.c.name = 'new copy'; console.log(target2); // { a: 1, b: 2, c: { name: 'new copy' } } console.log(copyTarget2); // { a: 1, b: 2, c: { name: 'new copy' } }
깊은 복사 방법
1.JSON.stringify() + JSON.parse()
JSON.stringify()는 JavaSciprt 값이나 객체를 json 문자열로 변환합니다. 그리고 이 과정에서 원본 객체와의 참조가 모두 끊어집니다. JSON.parse()는 JSON 문자열의 구문을 분석하고, 그 결과에서 JavaScript 값이나 객체를 생성하기 때문에 깊은 복사를 할 수 있습니다. 이 방벙은 쉽지만, 단점이 있는데 객체에 함수가 있는 경우 undefined로 처리되서 함수가 복사되지 않습니다.
// 중첩 객체 const target1 = { a: 1, b: 2, c: { name: 'copy', }, }; const copyTarget1 = JSON.parse(JSON.stringify(target1)); console.log(copyTarget1); // { a: 1, b: 2, c: { name: 'copy' } } copyTarget1.c.name = 'new copy'; console.log(target1); // { a: 1, b: 2, c: { name: 'copy' } } console.log(copyTarget1); // { a: 1, b: 2, c: { name: 'new copy' } } // 객체에 함수가 있는 경우 const target2 = { a: 1, b: 2, c: { name: 'copy', }, d: function () { console.log('JSON COPY'); }, }; const copyTarget2 = JSON.parse(JSON.stringify(target2)); console.log(target2); // { a: 1, b: 2, c: { name: 'copy' }, d: [Function: d] } console.log(copyTarget2); // { a: 1, b: 2, c: { name: 'copy' } }
2. 재귀 함수를 사용한 복사
// 객체에 함수가 있는 경우 const target = { a: 1, b: 2, c: { name: 'copy', }, d: function () { console.log('recursive'); }, }; function recursiveDeepCopy(obj) { if (obj === null || typeof obj !== 'object') { return obj; } const copy = Array.isArray(obj) ? [] : {}; for (let key of Object.keys(obj)) { copy[key] = recursiveDeepCopy(obj[key]); console.log(key, copy[key], copy); /* a 1 { a: 1 } b 2 { a: 1, b: 2 } name copy { name: 'copy' } c { name: 'copy' } { a: 1, b: 2, c: { name: 'copy' } } d [Function: d] { a: 1, b: 2, c: { name: 'copy' }, d: [Function: d] } */ } return copy; } const copyTarget = recursiveDeepCopy(target); console.log(target); // { a: 1, b: 2, c: { name: 'copy' }, d: [Function: d] } console.log(copyTarget); // { a: 1, b: 2, c: { name: 'copy' }, d: [Function: d] }
재귀를 위용한 깊은 복사는 함수도 잘 복사됩니다.
3. Lodash 라이브러리 이용 - cloneDeep 사용
정리
얕은 복사는 slice, assing, spread 3가지가 있고 깊은 복사는 JSON, 재귀함수, cloneDeep 3가지가 있다.
'Front-end' 카테고리의 다른 글
You are running `create-react-app` 4.0.3, which 에러 해결 (0) 2023.01.15 아주 쉽게 React + Typescript + TailwindCSS 세팅하기 (0) 2022.12.08