Preparing workspace
← 목록으로

[Tech Article] 깊은 복사와 얕은 복사에 대한 심도있는 이야기

Tech Article

2021.05.11

깊은 복사와 얕은 복사에 대한 심도있는 이야기



1. Array.prototype.slice

  • 주로 배열을 깔끔하게 복사할 때 사용할 수 있음.
    Array.prototype.slice는 start 부터 end 까지 대상 배열에서 꺼내서
    새로운 배열을 만들어 값을 집어 넣음.
    start 와 end 가 주어지지 않으면 전체 배열을 복사함!
  • Array.prototype.slice중첩 구조 복사를 제대로 수행할 수 없다 는 단점이 있음.
  • Array.prototype.slice얕은 복사!

2. Spread Operator (펼침 연산자, 전개 연산자)

  • 펼침 연산자(Spread Operator)의 등장으로 값의 복사가 전반적으로 매우 편리.
  • 펼침 연사자가 호출될 때, 내부적으로는 iterator-looping 액션을 수행.
  • 예) 어떤 객체에 [Symbol.iterator] 프로퍼티가 존재한다면 이터러블하다고 할 수 있고 반복문을 돌릴 수 있음
  • 만약 const arr = [1, 2, 3]; -> const arrSpread = [...arr]; 이렇게 펼침 연산자를 사용한다면
if (!(Typeof(b) is Iterable)) {
  throw TypeError
}
var a = [];
for (var i = 0; i < b.length; i += 1) {
  a.push(b[i]);
}

이러한 형식으로 돌아간다. (이거보다 더 훨씬 많은 검사과정이 있겠지만..?)

  • 펼침 연산자도 위와 같은 구조로 돌아가기에 중첩 구조는 복사하지 못한다!

3. Object.assign

  • Object.assign 으로 객체를 복사는 불가능하다.
  • 코드 예시 (복사 불가능)
    const arr = [1, 2, [3, 4]];
    const copied = Object.assign([], arr);
    checker(arr, copied); // true
    copied[2].push(5);
    checker(arr, copied); // true
    // ~~~~~~~
    // It should've been false
    
  • 심층 복제의 경우 Object.assign ()이 속성 값을 복사하기 때문에 대안을 사용해야 함.
    소스 값이 개체에 대한 참조 인 경우 해당 참조 값만 복사함. (MDN의 Object.assign 번역)

4. JSON.parse & JSON.stringify

  • JSON.stringify 는 중첩 구조를 복사할 수 없는 이터러블 순회를 해결할 수 있다.
    (입력 값으로 넘어온 데이터를 문자로 변형을 시켜주는 메소드)
  • JSON.parseJSON.stringify등으로 변형된 문자를 다시 원래 객체로 되돌려주는 역할을 함.
  • 코드 예시
    const arr = [1, 2, [3, 4]];
    const copied = JSON.parse(JSON.stringify(arr));
    checker(arr, copied); // true
    copied[2].push(5);
    checker(arr, copied); // false
    
    • 중첩구조 복사 역시 아주 깔끔하게 수행된 것을 볼 수 있다.
      객체 순환을 통해 값을 옮겨담는 과정이 아닌, 문자열로 변경 후
      그것을 다시 해석해 원본 객체로 변경하는 과정에서 자바스크립트의 문자열(string)타입이
      불변성 형질의 띠는 immutable primitive type (원시타입)이기 때문.
  • 코드 예시 - 예외 1 (Date)
    const obj = { d: new Date() };
    const copied = JSON.parse(JSON.stringify(obj));
    checker(obj, copied); // false
    // 웨.. 안같아?
    
    • Date 객체가 JSON.stringify 의 입력 값으로 전달되면 JSON.stringify
      Date 객체의 toString 메소드를 실행.
      같은 시간 같은 날짜를 가리키는 데이터라고 해도 보여지는 방식이 달라졌기 때문에
      두 값의 서로 다른 값으로 간주되는 것
  • 코드 예시 - 예외 2 (ECMA2020의 BigInt)
    JSON.stringify(BigInt(1));
    // ~~~~~~~~~~~~~~
    // Uncaught TypeError
    // Do not know how to serialize a BigInt
    
    • BigInt의 경우 아예 오류 반환..
  • Array.prototype.slice 와 같은 예제에서 보여준 메소드들과는
    다르게 JSON.stringify 는 무한 사이클을 갖는 구조를 받지 않음.
    const a = [];
    a[0] = a;
    JSON.stringify(a);
    // Uncaught TypeError
    // Converting circular structure to JSON
    
    • 이 경우는 순회하지 않는 경우를 뜻하는 “acyclic” 이라는 단어가 아예 ECMA-262 명세서에 따로 언급되고 있음
      함수 역시 JSON.stringify문자로 바꿀 수 없는 객체 중 하나다.
      const arr = [function(){}, () =>{}];
      const copied = JSON.parse(JSON.stringify(arr));
      checker(arr, copied); // false
      
  • JSON.stringify는..
    • JSON 객체에 대한 명세서 따로 존재하는데, ECMA 에서 관리되고 있으며 번호는 404 를 부여받음.
      그래서 ECMA-404 라고 통칭한다
    • ECMA-404 (JSON 명세서)의 JSON 값으로 표현될 수 있는 종류.
      • Object, array, number, string, true, false, null
      • JSON 값으로 판단될 수 있는건 ECMA-404 에 따르면 위 처럼 7 가지 종류만 포함
        나머지는 JSON 으로 분류될 수 없기에 JSON.stringify로 제대로 변환이 안될 수 있다.
        이에 관한 내용은 아래 기술문서를 보자!

느낀점

  • 음.. 이터러블이랑 Object.assign을 알고나서 또 다시 읽는 것도 좋을 것 같다. 또 알아야 할 것은 자바스크립트의 최신 기능들. 그래도 더 확고히 복사에 대해 이해가 어느정도 되었고, 종은 문서였다.

참고 링크

Ranohub

0%