깊은 복사와 얕은 복사

깊고 얕은 복사를 탐구하기 전에 JavaScript의 변수 유형을 이해해야 합니다.

원시 유형

  • 데이터가 복사되면 독립적인 값을 저장하기 위해 새로운 공간이 할당됩니다.

  • 문자열 / 숫자 / 널 / 부울 / 정의되지 않음 / 기호
  • 변수에 할당되면 원시 값은 고정된 크기로 메모리에 저장되고 변수는 저장된 값을 직접 가리킵니다.

  • 값은 불변이므로 재할당하면 원래 값이 바뀌는 것처럼 보이지만 실제로는 재할당된 값이 새 메모리에 저장되고 변수가 가리키는 메모리가 변경된다.

    • 원시 값은 변경할 수 없지만 변수 값은 재할당을 통해 언제든지 변경할 수 있습니다.

참조 유형

  • 기본 유형을 제외한 나머지는 참조 유형(객체)이라고 할 수 있습니다.

  • 메모리는 메모리에 직접 액세스하는 것이 아니라 메모리의 위치(주소)에 대한 간접 참조를 통해 액세스됩니다.

    • 변수의 크기가 동적으로 변하기 때문에 객체 데이터 자체는 별도의 메모리 위치(heap)에 저장되며, 변수에 할당되면 데이터(heap)의 주소를 저장하여 값에 액세스합니다.

  • 개체에 할당된 변수의 경우 “변수가 00이라는 값을 갖는다”보다는 “변수가 개체를 참조합니다” 또는 “변수가 개체를 참조합니다”라고 말하는 것이 더 정확합니다.

  • 속성을 사용하면 속성을 동적으로 추가, 업데이트 또는 삭제할 수 있습니다.

변수의 종류에 대해 간단히 알아보았으니 이제 복사에 대해 알아봅시다!

플랫 카피

  • 개체를 복사할 때 원래 값과 복사된 값은 동일한 참조를 가리킵니다.

  • 객체 내에 객체가 존재하고 객체가 기존 변수의 객체를 참조하는 경우 이를 얕은 복사라고 합니다.

  • 복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 동일한 메모리 주소를 참조합니다.

    • 따라서 해당 메모리의 주소 값이 변경되면 원본 객체와 복사 객체의 인스턴스 변수 값도 변경됩니다.

◇ 평면 복사 방식

  • 확산 연산자
    • 반복 가능해야 합니다.

      (속성(Symbol.iterator)이 존재하는 경우 iterable이라고 할 수 있습니다.

      )
  • object.assign()
  • 디스크()
  • 배열.from()

※ 중첩 구조의 객체를 복사할 때 한 레벨(1 깊이)까지만 복사하고, 깊은 객체(2 깊이)에 중첩된 객체의 주소 값(참조 값)을 복사합니다.

깊은 복사

  • 개체를 복사할 때 개체와 인스턴스 변수가 복사된다는 의미입니다.

  • 복사된 인스턴스는 원래 개체와 완전히 독립적이며 참조가 완전히 끊어진 개체로 볼 수 있습니다.

◇ 딥카피 방식

  • JSON.stringify() / JSON.parse() 사용
    • 직렬화할 수 없는 속성에는 복사할 수 없다는 제한이 있습니다.

    • 단점은 함수/날짜 객체/정규식과 같은 객체 데이터가 복사되지 않는다는 점입니다.

    • 느린.
  • structureClone() 사용
  • Lodash 라이브러리에서 cloneDeep() 사용
  • 재귀 함수 구현 복사
    • MDN 추천(?) 방법이었습니다!

※ JSON.stringify() 및 structureClone()은 딥카피 방식이 아닌 엄격한 직렬화 방식입니다.

※※Serialization은 Object 형식의 데이터를 Depth가 없는 Byte 형식으로 변환하는 작업입니다.

직렬화는 원본을 파괴하지 않고 데이터 전송을 위한 새로운 데이터를 생성하며 원본에서 값을 복사하므로 깊은 복사가 이루어집니다.

그래서 직렬화를 통해 딥카피가 가능합니다!

“그럼 map() / filter() /reduce() 함수를 사용하는 카피는 어떤 건가요?” 질문을 받았는데 오답(?)이 나와서 다시 확인했습니다.

세 가지 모두 플랫 카피입니다!
!

let array = (1,2,3,true,{myName: '현정', age: 27})

// map 함수로 복사
let mapCopy = array.map((el) => el);

// filter 함수로 복사 
let filterCopy = array.filter((el) => true);

// reduce 함수로 복사 (비효율적인 방법이라고 한다)
let reduceCopy = array.reduce((acc, cur)  => {
    acc.push(cur)
    return acc;
},())

알면 알수록 자바스크립트(?)에 대해 더 많이 알아야 합니다.