파트 1 - 3) 객체: 기본 - 2) 참조에 의한 객체 복사

Lee·2021년 10월 13일
0

참조에 의한 객체 복사 파트: https://ko.javascript.info/object-copy

참조에 의한 객체 복사

원시값에는 값들이 그대로 저장, 할당, 복사 되고 객체에는 값들이 참조에 의해 저장되고 복사된다.

만약 변수에 객체를 저장하면 객체 그대로 저장되는 것이 아니라 객체가 저장되어있는 객체에 대한 참조 값(메모리 주소)이 저장된다.
(C언어의 포인터가 메모리 주소를 참조하던 것이 생각난다.)

따라서 객체가 할당된 변수를 복사하면 객체의 참조 값이 복사되고 객체는 복사되지 않는다!

<script>
let user = { name : 'John' };
let admin = user; 
//admin은 user객체 자체가 아니라 user객체에 대한 참조값이 복사된 변수.
</script>

따라서 아래와 같이 admin으로 user객체에 접근해도 user객체에 대한 참조값으로 접근하는 것이기 때문에 user객체의 값을 수정할 수 있다.

<script>
  let user = { name: 'John' };

  let admin = user;

  admin.name = 'Pete'; // 'admin'에 저장된 user객체에 대한 참조 값에 의해 변경됨

  alert(user.name); // 'Pete'
</script>

참조에 의한 비교

객체 비교 시 동등 연산자 ==와 일치 연산자 ===는 동일하게 동작한다.
동등 연산자, 일치 연산자는 피연산자가 되는 객체들이 같은 객체일 경우 참을 반환한다.

<script>

  let a = {};
  let b = a; // 참조에 의한 복사

  //a와 b는 같은 객체를 참조하기 때문에 true가 반환된다.
  alert( a == b ); // true
  alert( a === b ); // true

//------------------------------------------------------

  let a = {};
  let b = {};

  // a와 b는 둘 다 비어있는 객체를 할당받아 얼핏 보면 같은 값처럼 보이지만 각자 독립된
  // 객체 참조값을 갖고 있으므로 같지 않다.
  alert( a == b ); // false
  
</script>

객체 복사, 병합과 Object.assign(얕은 복사를 수행)

같은 객체를 바라보는 게 아니라 기존에 있던 객체와 똑같으면서 독립적인 객체를 만들고 싶다면 어떻게 해야할까?!

객체를 복사하는 내장 함수는 없다.
1) 대신 아래와 같이 반복문으로 복사할 객체 안의 값들을 읽어와 빈 객체에 복사할 수 있다. (사실 객체를 복사할 일은 잘 없다.)

<script>
  let user = {
    name: "John",
    age: 30
  };

  let clone = {}; // 새로운 빈 객체

  // 빈 객체에 user 프로퍼티 전부를 복사해서 할당한다.
  for (let key in user) {
    clone[key] = user[key];
  }

// clone 객체 안에는 user 객체와 같은 key와 값을 가진 프로퍼티들이 저장 되어있다.
  clone.name = "Pete"; // clone 객체의 값을 변경

  alert( user.name ); // user객체에 있던 기존 값인 'John'이 출력된다. 
  //수정한 건 user 객체의 값이 아니라 clone객체의 값이니까.
</script>

2) Object.assingn을 이용하는 방법도 있다.

기본적인 사용법 : 
Object.assingn(복사한 값을 담을 객체(=목표객체), 복사할 객체1, 복사할 객체 2 ...);

<script>

// 예시 
  let user = { name: "John" };

  let permissions1 = { canView: true };
  let permissions2 = { canEdit: true };

  // user 객체에 permissions1과 permissions2의 프로퍼티를 복사한다.
  Object.assign(user, permissions1, permissions2);

  // user = { name: "John", canView: true, canEdit: true }
</script>

만약 복사한 값을 담을 객체에 복사할 객체가 가진 프로퍼티와 동일한 이름의 프로퍼티가 있다면 복사한 값이 덮어씌워진다.

변수에 assign문을 할당하고 빈 객체에 다른 객체를 복사하면 복사된 객체의 프로퍼티들이 빈 객체에 담기고 해당 변수가 그 객체를 갖게된다. (아래 예시)


중첩 객체 복사

위의 예시들은 프로퍼티의 값이 원시값인 경우들이었다.
만약 프로퍼티가 다른 객체에 대한 참조값을 가졌다면?

<script>
  let user = {
    name: "John",
    sizes: {
      height: 182,
      width: 50
    }
  };

  alert( user.sizes.height ); 
  // 182 (user 않의 sizes 프로퍼티가 가진 객체를 참조한 값이다)
</script>

만약 위에 있는 sizes 프로퍼티를 복사할 경우 clone.sizes = user.sizes로는 객체를 복사할 수 없다.
user.sizes가 객체이기 때문에 user.sizes에 대한 참조 값이 복사되기 때문이다. clone.sizes = user.sizes로 프로퍼티를 복사하면 clone과 user는 같은 sizes를 공유하게 된다.
즉, clone.sizes를 변경하면 user.sizes의 값이 함께 변경된다...^^

깊은 복사

이 문제를 해결하려면 user[key]의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사해주는 반복문을 사용해야 한다. 이런 방식을 '깊은 복사(deep cloning)'라고 한다.

1) 깊은 복사 시 사용되는 표준 알고리즘인 Structured cloning algorithm을 사용하면 위 사례를 비롯한 다양한 상황에서 객체를 복제할 수 있다.

2) 자바스크립트 라이브러리 lodash의 메서드인 _.cloneDeep(obj)을 사용하면 이 알고리즘을 직접 구현하지 않고도 깊은 복사를 처리할 수 있다.

profile
하고 싶은 게 너무 많습니다.

0개의 댓글

관련 채용 정보