변수를 복사하는 과정은 기본형 데이터와 참조형 데이터 모두 같은 주소를 바라보게 되는 점에서는 동일하지만,
데이터 할당 과정에서 이미 차이가 있기 때문에 변수 복사 이후의 동작에서 큰 차이가 발생
코드 예제
분명 obj2.c의 값을 변경하였는데, obj1.c의 값도 같이 변경이 되었다 왜그럴까?
변수 a와 b는 서로 다른 주소(데이터)를 바라보게 됬지만, 변수 obj1과 obj2는 여전히 같은 객체를 바라보고 있다.
대부분의 자바스크립트 책에서 '기본형은 값을 복사하고 참조형은 주솟값을 복사한다'고 설명하고 있지만, 사실은 어떤 데이터 타입이든 변수에 할당하기 위해서는 주솟값을 복사해야 하기 때문에, 엄밀히 따지면 자바스크립트의 모든 데이터 타입은 참조형 데이터일 수 밖에 없다.
다만, 기본형은 주솟값을 복사하는 과정이 한 번만 이루어지고, 참조형은 한 단계 더 거치게 된다는 차이가 있다.
코딩 예시
obj1!==obj2가 성립하게 된다.
참조형 데이터가 '가변값' -> '가변'은 참조형 데이터 자체를 변경할 경우가 아닌, 그 내부의 프로퍼티를 변경할 때만 성립한다.
불변 객체
참조형의 '가변'은 데이터 자체가 아닌 내부 프로퍼티를 변경할 때만 성립한다. 데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않는다.
내부 프로퍼티를 변경 할 필요가 있을 때마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 자동으로 새로운 객체를 만드는 도구를 활용한다면 객체 역시 불변성을 확보할 수 있다. 혹은 불변성을 확보할 필요가 있을 경우에는 불변 객체로 취급하고, 그렇지 않은 경우에는 기존 방식대로 사용하는 식으로 상황에 따라 대처해도 된다.
어떠한 상황에서 불변 객체가 필요할까?
- 값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우
가변성으로 인한 문제점 예시
user의 이름은 sangyun으로, user1의 이름은 daeyoung으로 갖도록 하고싶은데, 참조형 참조 방식 특성 상
같은 주소를 갖기 때문에 데이터가 변경되면, 같이 변경이 되는 문제점이 생긴다.
이런 방식으로 새로운 객체를 반환해 주면...
참조로 인한 첫 예제에서 나왔던 문제가 해결된다! user와 user2는 완전히 서로 다른 객체이다
위 예시는 미흡한 점이 존재 -> 새로운 객체를 만들면서 변경할 필요가 없는 기존 객체의 프로퍼티(gender)를 하드코딩으로 입력했다.
이런 식으로 하면 대상 객체에 정보가 많을 수록, 변경해야 할 정보가 많을수록 사용자가 입력하는 수고가 늘어난다. 이러한 방식보단 대상 객체의 프로퍼티 개수에 상관 없이 모든 프로퍼티를 복사하는 함수를 만드느 편이 더 좋다.
for in 문법을 이용해서 result 객체에 target 객체의 프로퍼티들을 복사해준다. 아직도 미흡한 점이 있지만, 전에 하드코딩을 했던 방식보다는 더 좋아보인다.
협업하는 모든 개발자들이 user 객체 내부의 변경이 필요할 때는 무조건 copyObject 함수를 사용하기로 합의하고 그 규칙을 지킨다는 전제하에서는 user객체가 곧 불변 객체라고 볼 수 있다. 하지만 모두가 이 규칙을 지키라는 인간의 신뢰에만 의존하는 것은 얇고 깨지기 쉬운 살얼음판을 걷는 것과도 같다. 그보다는 모두가 그 규칙을 따르지 않고는 프로퍼티 변경을 할 수 없게끔 시스템적으로 제약을 거는 편이 안전하다. 이런 맥락에서 immutable.js, baobab.js등의 라이브러리가 등장해서 인기를 끌고 있다.이들은 자바스크립트 내장 객체가 아닌 라이브러리 자체에서 불변성을 지닌 별도의 데이터 타입과 그에 따른 메서드를 제공한다.
얕은 복사와 깊은 복사
얕은 복사 -> 바로 아래 단계의 값만 복사하는 방법
깊은 복사 -> 내부의 모든 값들을 하니하나 찾아서 전부 복사하는 방법
copyObject 함수의 얕은복사 -> 중첩된 객체에서 참조형 데이터가 저장된 프로퍼티를 복사할 때 그 주솟값만 복사한다.
해당 프로퍼티에 대한 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 된다. 사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀐다.
중첩된 객체에 대한 얕은 복사
user2의 name 프로퍼티를 바꿔도 user의 name 프로퍼티는 바뀌지 않는다.
반면, user.urls.portfolio, user2.urls.blog의 값은 원본과 사본 중 어느 쪽을 바꾸더라도 다른 한쪽의 값도 함께 바뀐 것을 확인할 수 있다. 즉, user 객체에 직접 속한 프로퍼티(name)에 대해서는 복사해서 완전히 새로운 데이터가 만들어진 반면, 한 단계 더 들어간 urls의 내부 프로퍼티들은 기존 데이터를 그대로 참조 하는 것이다. 이런 현상이 발생하지 않게 하려면 user.urls 프로퍼티에 대해서도 불변 객체를 만들 필요가 있다.
user의 내부 프로퍼티인 urls도 copyObject를 이용해서 객체를 복사를 해준다. -> 깊은 복사
이렇게 해주게 되면, portfolio와 blog의 값이 같이 않게 된다.
왜? urls가 가리키는 data영역의 주솟값이 서로 다른 것을 참조하고 있기 때문이다.
깊은 복사 함수
target의 타입이 object이거나 target이 null이 아닌경우
반복문을 이용해서 target의 내부 프로퍼티를 깊은 복사를 해준다.
단,
이러한 경우에는 또다시
원본과 복사본의 값이 같이 바뀌는 것을 확인할 수 있다.
또 다른 깊은 복사를 처리할 수 있는 방법
객체를 JSON문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 것
JSON으로 하게되면
copyObjectDeep의 문제점을 해결해 줄 수 있다.
'2022년' 카테고리의 다른 글
undefine와 null (0) | 2021.12.11 |
---|---|
엘리멘트 렌더링 (0) | 2021.12.08 |
1.1 데이터 타입의 종류 (0) | 2021.12.04 |
프래그먼트 트랜잭션 (0) | 2021.12.04 |
프래그먼트 관리자 (0) | 2021.12.04 |