얕은 복사(Shallow Copy)는 참조값의 복사를 나타낸다.
const obj = { a: 1 };
const copyObj = obj;
copyObj.a = 2;
console.log(obj.a); // 2
console.log(obj === copyObj); // true
obj라는 객체를 copyObj라는 객체에 복사하여 copyObj.a값을 변경하였더니 기존의 obj.a값도 같이 변경되었다.
마찬가지로 두 객체를 비교해봐도 true가 나온다.
이렇게 자바스크립트의 참조 타입은 얕은 복사(Shallow Copy)가 된다고 볼 수 있으며,
이는 데이터가 그대로 생성되는 것이 아닌 해당 데이터의 참조 값을 전달하여 한 데이터를 공유하는 것이다.
깊은 복사는 얕은 복사(Shallow Copy)처럼 참조값이 복사되는 것이 아니라, 값만 복사되는 것을 의미한다.
const a = 1;
const b = a;
b = 2;
console.log(a); // 1
console.log(b); // 2
console.log(a === b); // false
위 코드는 변수 a 에 1을 할당하고, 변수 b 에 a 의 값을 할당한 코드이다.
변수 b 의 값을 변경하여도 기존의 a 의 값은 변경되지 않는다.
두 값을 비교하여도 false가 출력되며 서로의 값은 단독으로 존재한다는 것을 알 수 있다.
위 예제처럼 자바스크립트의 원시 타입(String , Boolean, number ...)
은 깊은 복사(Deep Copy)가 되며,
이는 독립적인 메모리에 값 자체를 할당하여 생성하는 것이라고 볼 수 있다.
💡 객체의 깊은 복사
객체를 그대로 복사하여 사용하면 기존 객체의 데이터가 변경될 수 있기 때문에
객체의 값을 복사하여 사용하고 싶다면 얕은 복사(Shallow Copy)가 아닌 깊은 복사(Deep Copy)를 사용하는 것이 좋다.
const newObject = JSON.parse(JSON.stringify(oldObject));
object를 stringify -> parse 과정을 통한 deep copy인데 함수를 포함하는 경우에는 복사되지 않는다.
ex)
- 함수를 포함하는 경우:
const oldObject = {a:'1',b: () => {console.log('hi');}};
const newObject = JSON.parse(JSON.stringify(oldObject));
console.log(newObject); // {a: "1"}
ES6 spread 문법을 사용해서도 deep copy가 가능하나 1 level(dimension)의 array, object에서만 deep copy가 가능하다.
ex)
const foo = [1,2,3];
const bar = [...foo]; // deepcopy
- 2~n차원 array에서는 shallow copy가 됨:
const foo = [[1,2],3];
const bar = [...foo];
foo[0].push(3);
console.log(bar); // [[1,2,3],3]
이후 소개할 lodash의 deepCopy 방법보다 성능은 더 좋아서 1차원의 object, array만 다루는 상황이면 사용해볼 만한 것 같다.
lodash deepCopy vs spread deepCopy benchmark
function deepCopy(obj) {
if(typeof obj !== 'object' || obj === null) {
return obj;
}
if(obj instanceof Date) {
return new Date(obj.getTime());
}
if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}
if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}
이후 소개할 lodash를 사용한 deep copy와 비교했을때, lodash를 사용하지 않는 환경이라면 이 방법을 사용해도 좋을 것 같다. deep copy만을 위해 사이즈 큰 lodash를 import 하기에는 비효율적이라고 생각한다.
몇 가지 모듈 크기 비교 시각화
4. lodash의 cloneDeep 사용하기
lodash에서 cloneDeep이라는 함수로 간단하게 deep copy를 할 수 있다.
가장 간단하고 명확한 방법으로 대체적으로 해당 방법을 추천한다.
import _ from "lodash";
const newObj = _.cloneDeep(obj);