[책정리] CoreJavaScript 1-5 불변객체

이진규·2022년 7월 15일
1
post-thumbnail

Q. 객체가 뭔가요?

 JS에서 객체란 하나의 데이터 타입인데 참조형 데이터 타입이다.

Q. 참조형 데이터가 뭔가요?

 JS에서 크게 두 데이터타입으로 나눌 수 있는데, 기본형 데이터타입과 참조형 데이터타입으로 나눌 수 있다.

Q. 데이터 타입은 뭐고 두 데이터타입은 뭐가 다른가요?

 데이터 타입은 변수가 가질 수 있는 데이터의 종류라 할 수 있다. 기본형 데이터 타입에는 숫자, 문자열 등이 있고 참조형 데이터 타입에는 배열, 날짜 등이 있다.
 기본형과 참조형의 가장 큰 차이는 기본형은 불변성을 띈다는 것이다.

Q. 뭐가 변하지 않는다는 건가요?

 결론부터 말하자면 기본형 데이터타입은 참조하는 데이터영역의 값이 변하지 않는다. 이것을 이해하기 위해서 변수를 선언하고 거기에 값을 할당해보자.

var z = 'qwer';

  위의 그림을 예로 들면 비어있는 변수영역에 선언된 변수 z의 이름을 넣고 비어있는 데이터영역의 주소를 찾고 그 주소에 대입할 값인 'qwer'을 넣고 변수영역은 그 주소를 참조하게 된다. 이제 변수에 대입한 값을 바꿔보자.

z = 'asdf';

  z에 'asdf'를 대입한다고 했을 때, 변수에 대입할 값을 할당해두었던 202번 주소의 값을 바꾸는 것이 아닌 다른 비어있는 주소인 203번 주소에 'asdf'를 넝고 102번 주소가 203번 주소를 참조하게 된다.

 결국, 데이터 영역이 변할 수 없기 때문에 202번 주소의 값인 'qwer'을 바꾸지 않고 새 데이터 영역을 할당해서 'asdf'를 넣고 변수 영역이 참조하는 데이터 영역의 주소를 바꾸게 된다.

Q. 객체는 어떻게 바뀌게 되나요?

우선 객체가 주소에 어떻게 할당되는지 확인해보자.

var object = {
 	attr1: 1,
  	attr2: 'zxcv'
};

 객체와 기본형 데이터의 차이점은 객체의 변수영역이 추가된다는 것이다. 객체는 내부에 key-value쌍의 데이터들이 담겨있고 key-value쌍을 프로퍼티라 부른다. 객체의 변수영역이란 key의 이름을 저장하는 변수 영역이고 value는 기존의 데이터 영역에 저장되게 된다. 그럼 이제 객체의 값을 변경시켜 보자.

object.attr1 = 20;

 위의 그림과 같이 변수영역이 참조하는 데이터 영역의 주소와 데이터영역이 참조하는 객체의 변수영역 주소는 바뀌지 않고 객체의 변수영역인 501번 주소가 참조하는 주소값을 바꿔주게 된다. 객체는 가변성의 성격을 띄고 바뀔 수 있기 때문에 데이터영역에 새롭게 주소를 할당할 필요 없이 객체의 변수영역의 값을 바꿔주게 된다.

Q. 그래서 참조형이랑 기본형이랑 뭐가 다른건가요?

 위의 동작방식에 따라 변수를 복사해서 변경할 때 가장 큰 차이가 보입니다. 기본형 데이터타입인 문자열 변수를 복사해보자.

var z = 'qwer';
var x = z;


 복사하면 같은 주소를 참조하게 된다. 이제 복사한 x를 바꿔보자.

x = 'asdf';


 위에서 설명했듯이, x에 새로운 값을 대입하기 위해 데이터 영역에 비어있는 주소인 203번 주소에 'asdf'를 넣고 참조하던 주소를 203번 주소로 변경하였다. 이제 참조형 데이터타입인 객체 변수를 복사해보자.

var obj = {
 	attr1: 1,
  	attr2: 'zxcv'
};
var obj2 = object;


 객체도 복사하면 같은 주소를 참조하게 된다. 이제 복사한 obj2의 attr1를 바꿔보자.

obj2.attr1 = 3;


 이 부분도 위에서 설명했듯이, 변수영역이 참조하는 데이터 영역의 주소와 데이터영역이 참조하는 객체의 변수영역 주소는 바뀌지 않고 객체의 변수영역인 501번 주소가 참조하는 주소값을 바꿔줍니다. 그러다보니 obj2의 attr1만 바꿔주고 싶었지만, obj의 attr1도 바뀌게 됩니다.

obj2.attr1 === 3 // true
obj.attr1 === 3 // true

 이렇게 가변성이 문제가 되기 때문에 불변성을 가진 객체를 불변객체 라 합니다.

Q. 그럼 불변객체는 어떻게 만드나요?

 참조형 데이터의 가변성은 내부 프로퍼티의 값을 변경할 때만 성립되고 새로운 데이터를 할당하여 데이터 영역을 변경하려고 하면 성립되지 않습니다. 이를 이용하여 객체를 복사하는 것이 아닌 내부 프로퍼티의 값을 복사하면 해결됩니다. 함수로 만들어봅시다.

var copyObj = function(targetObj){
  var copy = {};
  for(var prop in targetObj){
    copy[prop] = targetObj[prop];
  }
  return copy;
};

 copyObj함수는 targetObj객체의 프로퍼티들을 복사하므로 이전과 같은 문제가 발생하지 않습니다. 하지만 이 때, 프로퍼티에 또 다른 참조형 데이터가 있다면 또 문제가 발생합니다. copyObj같은 경우를 '얕은 복사'라고 하고 발생한 문제를 해결하기 위한 복사 방법을 '깊은 복사'라 합니다.

Q. 깊은 복사는 어떻게 해야하나요?

 개념적으로 깊은 복사는 참조형 데이터 내부 프로퍼티에 참조형 데이터가 있을 경우, 그 참조형 데이터 내부에 들어가서 복사하는, 말 그대로 깊이 복사하는 방법입니다. 이를 구현하기에는 복잡한 부분이 있기에, 코어 자바스크립트 책에서 간단하게 깊은 복사를 구현할 수 있는 방법을 소개합니다.

var copyObjViaJSON = function(targetObj){
  return JSON.parse(JSON.stringify(targetObj));
};

 객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON객체로 바꾸면 깊은 복사가 됩니다.
하지만 내부 프로퍼티가 함수일 때에나 숨겨진 프로퍼티인 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 복사하지 못하고 무시하게 됩니다.
이 방법은 순수한 데이터만을 다룰 때 굉장히 유용한 방법입니다.

마무리

 시작은 불변 객체로 시작했지만 불변 객체를 이해하기 위해 알아야 할 배경지식이 많았고 얕은 복사와 깊은 복사까지 다루면서 내용이 길어졌지만 오히려 한 부분만 다루지 않고 1과 내용을 전반적으로 다루게 되어서 좋았다.

참고

정재남, ⌜코어 자바스크립트⌟, 위키북스, 2022, 1 - 29쪽

profile
개발자

0개의 댓글