[TIL] 깊은 복사 관련 놓치는 부분

이진호·2023년 10월 17일
2

TIL

목록 보기
3/66

깊은 복사 관련되서 객체나 배열은 원시값이 아닌 call by reference 방식으로 데이터가 저장되기 때문에
아래와 같은 결과가 나오게 된다.

const obj = { name : 'lee', age: 20} ;
const obj2 = obj;
const obj3 = {name: 'lee', age: 20};
console.log(obj === obj2) // true;
console.log(obj === obj3) // false;

그래서 obj를 복사하기 위해서는 얕은 복사와 깊은 복사 개념이 있는데 이 중에서 깊은 복사와 관련된 내용에 대해서 "최00"님께서 고민하신 내용에 대해서 흥미로워서 글을 남긴다. 이 자릴 빌어서 다시 한번 감사하다는 말씀 올리겠습니다. 😍

문제

깊은 복사를 하는 방법으로 아래와 같은 함수 deepCopy를 사용해서 선언을 했는데
이 객체 내에 배열이 있을 경우 배열로 나오는 것이 아닌 객체의 형태로 원하지 않은 값이 나온다는게 문제였다.

var obj = {
  name: 'Choi',
  age: 30,
  obj2: {
    name: 'Shin',
    age: 27,
    obj3: {
      name: '애기',
      age: 1,
    },
  },
  arr: [1, 2, 3, 4, 5],
};

function deepCopy(obj) {
  var copy = {};

  for (let value in obj) {
    copy[value] = obj[value];

    if (typeof copy[value] === 'object') {
      copy[value] = deepCopy(obj[value]);
    }
  }

  return copy;
}

const a = deepCopy(obj);
console.log(a);
// a의 실행 결과
{
  name: 'Choi',
  age: 30,
  obj2: { name: 'Shin', age: 27, obj3: { name: '애기', age: 1 } },
  arr: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
}

문제 원인

  • deepCopy() 함수 내에서 for in문을 이용하여 객체를 반복하게 된다.
  • 배열도 객체이기 때문에 for in문을 이용하면 key값 즉, index값으로 순회하게 된다.

위와 같은 이유로 a.arr의 내용이 마치 객체처럼 표시되는 것이다.

문제 해결 방법

현재 키 값이 객체일 경우 그것이 배열인지 혹은 배열이 아닌지를 확인을 해야 한다.
배열의 기본적인 조건은 length와 각 인덱스가 key 값으로 들어갈 수 있다라는 점이다.
이를 기준으로 객체와 배열을 구분해 내고, 만약 배열의 경우 copy = {}이 아닌 copy = []를 사용하여 배열을 반환하게 끔 하였다.
추가적으로 배열 안에도 똑같이 객체나 배열이 있을 경우 이를 재귀적으로 처리할 수 있도록 구현하였다.

var obj = {
  name: 'Choi',
  age: 30,
  obj2: {
    name: 'Shin',
    age: 27,
    obj3: {
      name: '애기',
      age: 1,
    },
  },
  arr: [{ name: 'lee' }, { name: 'jin', arr: [1, 2, 3, 4, 5] }, 3, 4, 5],
};
const MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
const isArrayLike = function (list) {
  const length = list === null ? null : list.length;
  return typeof length === 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
function deepCopy(obj) {
  if (isArrayLike(obj)) {
    // 배열의 경우
    const copy = [];
    for (let i = 0; i < obj.length; i++) {
      if (typeof obj[i] === 'object') copy.push(deepCopy(obj[i]));
      else copy.push(obj[i]);
    }
    return copy;
  }

  var copy = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      copy[key] = deepCopy(obj[key]);
    } else {
      copy[key] = obj[key];
    }
  }

  return copy;
}

const a = deepCopy(obj);
console.log(a.arr === obj.arr); // false
console.log(a.arr[0] === obj.arr[0]); // false
console.log(a.arr[1].arr === obj.arr[1].arr); // false

느낀점

  • 현재는 객체와 배열을 구분하는게 length 와 key값이 정수의 형태라는 것 까지 밖에 모르겠다. 다음에 다른 방식으로 구분할 수 있는지에 대해서도 공부하면 좋겠다.
profile
dygmm4288

0개의 댓글