Shallow Copy & Deep Copy?? 뭔데?? 이거?

1
post-thumbnail

1. Shallow Copy(얕은 복사)와 Deep Copy(깊은 복사)이해

  • 얕은 복사는 객체의 참조 값(주소 값)을 복사하고, 깊은 복사는 객체의 실제 값을 복사한다.
    자바스크립트에서 값은 원시값과 참조값 두 가지 데이터 타입의 값이 존재한다.
  • 원시타입의 값은 새로운 메모리 공간에 독립적인 값을 저장하기 때문에 깊은 복사가 된다.
  • 참조타입의 값은 얕은 복사가 된다.
  • 두 타입의 큰 차이점은 원본이 바뀌면 참조 타입은 같이 변경되지만, 원시 타입은 변경되지 않는다.
// 원시 타입의 깊은 복사
let a = '원본 데이터';
let b = a;

a = '수정 데이터';

console.log(a); // '수정 데이터'
console.log(b); // '원본 데이터'
  • 원시 타입은 복사 시 값 자체를 담은 독립적인 메모리를 생성하기 때문에 a가 재할당 되더라도 b에 아무런 영향을 주지 않는다.
// 참조 타입의 얕은 복사
let a = {name:'원본 데이터'};
let b = a;

a.name = '수정 데이터';

console.log(a); // '수정 데이터'
console.log(b); // '수정 데이터'
  • 새로운 값으로 변수 값을 재할당 하자 변수값도 같이 변경되는것을 확인할 수 있다. 즉, 데이터가 그대로 하나 더 생성된 것이 아닌 해당 데이터의 메모리 주소를 전달하게 돼서, 결국 한 데이터를 공유하게 되는 것이다.

1 - 1. 얕은복사(Shallow Copy)

  • 객체를 복사 할 때, 해당 객체만 복사하여 새 객체를 생한다.
  • 복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조한다.
  • 따라서, 해당 메모리 주소의 값이 변경되면 원본 객체 및 복사 객체의 인스턴스 변수 값은 같이 변경된다.

1) Array.prototype.slice()

  • 얕은 복사의 한 예이다. start부터 end 인덱스까지 기존 배열에서 추출하여 새로운 배열을 리턴하는 메소드. 만약 start,end 설정을 하지 않는다면, 기존 배열을 전체 얕은 복사한다.
const original = ['a',2,true,4,"hi"];
const copy = original.slice();

console.log(JSON.stringify(original) === JSON.stringify(copy)); // true

copy.push(10);

console.log(JSON.stringify(original) === JSON.stringify(copy)); // false

console.log(original); // [ 'a', 2, true, 4, 'hi' ]
console.log(copy); // [ 'a', 2, true, 4, 'hi', 10 ]
  • 기존 배열에는 영향을 끼치지 않아서 깊은 복사로 보일 수 있지만, 원시값을 저장한 1차원 배열일 뿐이다. 원시값은 기본적으로 깊은 복사이다.
const original = [
  [1, 1, 1, 1],
  [0, 0, 0, 0],
  [2, 2, 2, 2],
  [3, 3, 3, 3],
];

const copy = original.slice();

console.log(JSON.stringify(original) === JSON.stringify(copy)); // true

// 복사된 배열에만 변경과 추가.
copy[0][0] = 99;
copy[2].push(98);

console.log(JSON.stringify(original) === JSON.stringify(copy)); // true

console.log(original);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
console.log(copy);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
  • 만약 1차원 배열이 아닌 중첩 구조를 갖는 2차원 배열이면 얕은 복사를 수행한다.
const original = [
  {
    a: 1,
    b: 2,
  },
  true,
];
const copy = original.slice();

console.log(JSON.stringify(original) === JSON.stringify(copy)); // true

// 복사된 배열에만 변경.
copy[0].a = 99;
copy[1] = false;

console.log(JSON.stringify(original) === JSON.stringify(copy)); // false

console.log(original);
// [ { a: 99, b: 2 }, true ]
console.log(copy);
// [ { a: 99, b: 2 }, false ]
  • 이 경우는 배열 안에 객체를 수정하고자 할 경우 얕은 복사를 수행하게 된다. 하지만, 원시값은 기본적으로 깊은 복사라서 기존 변수에 있는 값과는 다른 값을 도출하는것을 볼 수 있다.

2 ) Spread 연산자(전개 연산자)

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

const newObj = { ...obj };

newObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === newObj.b.c); // true
  • 위 예제를 보면 Spread연산자도 얕은 복사를 하는 것을 볼 수 있다.

1 - 2 깊은복사(Deep Copy)

  • 깊은 복사를 하는 목적은 기존 객체의 값만 복사본으로 가져와 별로도 활용하기 위함이다.

1 ) JSON.parse & JSON.stringify

  • JSON.stringify()는 객체를 json 문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어진다.
  • 객체를 json 문자열로 반환 후, JSON.parse()를 이용해 다시 원래 객체로 만들어준다.
const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

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

newObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 2 } }
console.log(obj.b.c === newObj.b.c); // false
  • 이 방법은 간단하고 쉽지만, 다른 방법에 비해 느리고 객체가 function 일 경우, undefined로 처리한다는것이 단점이다.
function 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 newObj = deepCopy(obj);

newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === newObj.b.c); // false
  • 이 문제를 해결하기 위해서는 깊은 복사를 구현하는 커스텀 재귀 함수를 사용하는 것이다. 하지만, 복잡하다.

마무리

=> 얕은 복사와 깊은 복사는 글로만 봤을때는 이해가 잘 되지도 않고, 그게 그거 인것처럼 보인다. 이것들은 다른 곳에서도 많이 활용 되므로, 많은 예제들을 참고해서 공부할 필요가 있어 보인다....ㅠㅠ

profile
끝날때 까지 끝난게 아니야. 결국 내가 이겨!

0개의 댓글

관련 채용 정보