얕은 복사 & 깊은 복사

졍이🥨·2023년 3월 9일
0

📝기술공부

목록 보기
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개의 댓글