얕은 복사 & 깊은 복사

졍이🥨·2023년 3월 9일

📝기술공부

목록 보기
21/40

얕은 복사(Shallow Copy) & 깊은 복사(Deep Copy)

얕은 복사는 주소값을 복사하기 때문에 참조하고 있는 실제 값은 같습니다.
깊은 복사는 실제 값을 메모리 공간에 복사하기 때문에 참조하고 있는 실제 값이 다릅니다.

얕은 복사를 구현하는 방법

1️⃣ Object.assign()

  • 문법: Object.assign(생성할 객체, 복사할 객체);
const obj = { a: 1 };
const copyObj = Object.assign({}, obj);

copyObj.a = 2;

console.log(obj); // { a: 1 }
console.log(obj === copyObj); // false

위 예제는 copyObj라는 객체를 Object.asign() 메소드를 사용하여 생성한 코드이다.

copyObj.a 값을 변경하여도 기존의 obj.a의 값이 바뀌지 않았다.

서로의 객체를 비교해도 false가 뜨며 서로 참조값이 다른 것을 알 수 있다.

📍 Object.assign()은 2차원 객체를 복사하였을 때 깊은 복사가 이루어지지 않는다 !

const obj = {
  a: 1,
  b: { c: 2 },
}

const copyObj = Object.assign({}, obj);

copyObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === copyObj.b.c); // true

위 예제는 obj라는 2차원 객체를 copyObj에 복사하고 copyObj.b.c 값을 변경한 예제이다.

기존 obj 객체를 출력해보면 Object.assign() 메서드를 사용하여 객체를 복사하였음에도 불구하고 값이 변경됐다.

이는 복사된 하위 객체 { c: 2 }도 결국 객체이기 때문에 얕은 복사(Shallow Copy)가 된 것이다.

그래서 Object.assign() 메서드는 1차원 객체만 깊은 복사(Deep Copy)해야하며, 전개 연산자(Spread Operator)도 마찬가지이다.

2️⃣ 전개 연산자(Spread Operator)

const obj = {
  a: 1,
  b: { c: 2 },
};

const copyObj = {...obj};

copyObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === copyObj.b.c); // true

전개 연산자(Spread Operator)도 Object.assign()과 마찬가지로 2차원 객체는 얕은 복사(Shallow Copy)가 되는 것을 확인할 수 있다.

깊은 복사를 구현하는 방법

3️⃣ JSON의 stringify()와 parse()

- stringify() 문법: JSON.stringify() 메소드는 객체를 인수로 받으며 받은 객체는 문자열로 치환

- parse() 문법: JSON.parse() 메소드는 문자열을 인수로 받으며 받은 문자열은 객체로 치환

const obj = {
  a: 1,
  b: { c: 2 },
};

const copyObj = JSON.parse(JSON.stringify(obj));

copyObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 2 } }
console.log(obj.b.c === copyObj.b.c); // false

위 예제는 obj 객체를 JSON.stringify() 메소드를 이용하여 문자열로 변환한 뒤

다시 JSON.parse() 메소드를 통해 객체로 변환한 예제이다.

2차원 객체도 깊은 복사(Deep Copy)가 되긴 하지만 2가지 문제가 있다.

1) 다른 방법에 비해 성능이 느리다.

2) JSON.stringify() 메소드는 함수를 만났을 때 undefined로 처리된다.

아래 예제 코드를 확인해보자.

const obj = {
  a: 1,
  b: { c: 2 },
  func: function() {
    return this.a;
  }
};

const copyObj = JSON.parse(JSON.stringify(obj));
console.log(copyObj.func); // undefined

복사된 copyObj는 func가 없고 undefined로 출력되고 있다.

4️⃣ 커스텀 재귀 함수

객체의 깊은 복사(Deep Copy)를 위해 직접 커스텀 재귀 함수를 구현하는 방법도 있다.

const deepCopy = (obj) => {
  if (obj === null || typeof obj !== 'object') return obj;
  
  let copy = {};
  for (let key in obj) {
    copy[key] = deepCopy(obj[key]);
  }
  return copy;
};

const obj = {
  a: 1,
  b: { c: 2 },
  func: function () {
    return this.a;
  }
};

const copyObj = deepCopy(obj);

copyObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === copyObj.b.c); // false

deepCopy() 함수로 obj 객체를 매개변수로 보낸 다음 인수값이 객체가 아닌 경우엔 그냥 반환하며,

객체인 경우 객체의 값 만큼 루프를 돌고 재귀를 호출하여 복사된 값을 반환한다.

복사된 copyObj 객체를 확인해보면 2차원 객체의 값도 깊은 복사(Deep Copy)가 되었으며, 객체의 함수도 제대로 출력되는 것을 확인할 수 있다.

5️⃣ lodash 라이브러리의 cloneDeep()

lodash 라이브러리의 cloneDeep() 메소드를 이용해 객체의 깊은 복사(Deep Copy)가 가능하다.

해당 라이브러리를 설치했다는 가정 하에 아래 코드를 확인해보자.

const obj = {
  a: 1,
  b: { c: 2 },
  func: function () {
    return this.a;
  }
};

const copyObj = lodash.cloneDeep(obj);

copyObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === copyObj.b.c); // false

lodash 라이브러리의 cloneDeep() 메소드를 이용하면 깊은 복사를 간편하게 구현할 수 있다.


💟 참고자료

깊은 복사를 구현하는 방법

profile
Front-End :)

0개의 댓글