코어 자바스크립트 - 데이터 타입 1

기운찬곰·2020년 10월 23일
0

Javascript

목록 보기
1/3
post-thumbnail

🤚 전체적인 내용은 위키북스 코어자바스크립트 도서, 정재남 지음 을 참고로 했고, 부가적인 내용과 생략된 부분이 존재할 수 있다.


데이터 타입

자바스크립트의 데이터 타입은 크게 두가지가 존재하는데 기본형과 참조형이다.

기본형(원시형, Prmitive)

  • 숫자(Number)
  • 문자열(String)
  • Boolean
  • Null
  • Undefined
  • Symbol(ES6에서 추가됨)

참조형(Reference)

  • 객체(object)
    • 배열(Array)
    • 함수(Function)
    • 날짜(Date)
    • 정규식(RegExp)
    • Map, WeakMap, Set, WeakSet(ES6에서 추가됨)

기본형 vs 참조형

기본형과 참조형을 나누는 기준은 무엇일까? 🤔

기본형은 값이 담긴 주솟값을 바로 복제하는 반면 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주소값을 복제한다는 점에서 다르다. 아직 잘 이해가 가지 않을텐데 이 글을 다 읽고나서 다시 한번 보기 바란다.


데이터 타입에 대한 배경지식

메모리와 데이터

보통 C나 C++, 자바 등의 정적타입 언어는 메모리의 낭비를 최소화하기 위해 데이터 타입별로 할당할 메모리영역을 1byte, 2byte, 4byte, 8byte 등으로 나눠놓은 것을 본 적이 있을 것이다.

하지만 자바스크립트는 과거보다 메모리 용량이 월등히 커진 상황에서 등장했기 때문에 상대적으로 메모리 관리에 대한 압박에서 자유로워졌다. 예를 들어 숫자의 경우 정수형인지 부동소수형인지 구분하지 않고 8byte를 확보해준다.

식별자와 변수

  • 변수 : 프로그램에서 사용되는 데이터를 일정 기간 동안 기억하여 필요한 때에 다시 사용하기 위해 데이터에 고유의 이름인 식별자를 명시한 것
  • 식별자 : 어떤 대상을 유일하게 식별할 수 있는 이름. 식별자에는 변수명, 함수명, 클래스명 등이 있다.

변수 선언과 데이터 할당

변수 선언

var a; // ES5 
let a; // ES6 이후

a 라는 식별자를 갖는 변수를 선언했다. 구체적으로 말하자면 a 라는 식별자는 특정메모리 주소를 가리키고 있고, 그 주소에 가면 비로소 저장된 데이터를 확인할 수 있는 것이다. 아무것도 할당하지 않으면 기본적으로 undefined로 초기화된다.

데이터 할당

let a = 'abc';

변수명 a가 가리키는 메모리 주소를 찾아서 'abc'를 할당하면 될거 같지만 세세히 따져봤을때는 틀린말이다. 😲 실제로는 데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보해서 문자열 'abc'를 저장하고 그 주소를 변수영역에 저장하는 식으로 이루어지기 때문이다.

이를 책에서는 변수영역데이터영역이라는 용어로 구분해서 설명하고 있다. (정식 명칭은 아님)

a는 변수영역에서 @1002라는 주소에 데이터를 가지고 있는데, 이 데이터는 다시 데이터 영역에 @5002번지라는 주소에 실제 'abc'를 저장해놓고 있다.

왜 이렇게 번거롭게 저장하는거지?

이는 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위한 고민의 결과이다. 예를 들어 다음 코드를 생각해보자.

let a = 'abc';
a = 'abcdef';

데이터영역에 'abcdef'라는 새로운 값이 저장될 것이고, 변수영역은 기존의 @5002번지 대신 @5003번지를 가리키게 된다.

이렇게 쓰는 이유는 두가지 이점이 있기 때문이다.

첫번째 이유

만약 데이터영역이 없었고 이걸 변수영역에서 한번에 처리하려고 했다면 골치아파질 것이다. 숫자형은 8btye이고 영어는 1byte, 한글은 2btye 등으로 각각 필요한 메모리 용량이 가변적이며 'abc'에서 'abcdef'로 바꿔주는 것 자체가 3btye에서 6btye로 메모리 크기 또한 재조정이 필요하다. 운이 나쁘게 뒤에 데이터가 있다면 전부 옮겨야하는 불상사가 발생하게 된다. (그건 데이터 영역도 마찬가지 아닌가...???)

두번째 이유

두번째 이유는 일례로 변수를 500개를 생성하고 모든 변수에 동일하게 숫자 5를 할당한다고 가정해보자. 만약 변수영역에 직접 값을 저장할 경우 500(변수개수) * 8byte(숫자형) = 4,000byte가 필요하다. 하지만 데이터영역에 저장하고 주소를 저장하는 경우라면 500(변수개수) * 2 (주소저장에 필요한 크기 가정) + 8byte(숫자형) = 1,008byte 이므로 훨씬 이득이다. (이건 인정 😲)


기본형 데이터와 참조형 데이터

불변값

기본형 데이터는 모두 불변값(immutable)이다. 왜 불변값인지는 다음 코드를 살펴보자.

let a = 'abc;
a = a + 'def';

let b = 5;
let c = 5;
b = 7;

변수 a에 문자열 'abc'를 할당했다가 뒤에 'def'를 추가하면 기존의 'abc'가 'abcdef'로 바뀌는 것이 아니라 새로운 문자열 'abcdef'가 만들어 그 주소가 변수 a에 저장된다. 따라서 기존의 'abc'는 그대로이며 새로운 'abcdef'를 만들었으니 불변값이라고 말할 수 있다.

변수 b에 숫자 5를 할당하고, c도 마찬가지로 5를 할당하는데 이미 만들어놓은 5가 있으므로 이 주소를 재활용한다. b의 값을 7로 바꾸면 기존에 5를 7로 바꾸는 것이아니라 7을 찾아서 있으면 재활용하거 없으면 새로 만들어서 b에 저장한다. 결국 5와 7 모두 다른 값으로 변경할 수 없다. 따라서 이 역시 불변값이라고 말할 수 있다.

✍ 여기서 헷갈리면 안되는게 a와 b는 값이 변했는데 가변값이 아닌가? 라고 생각할 수 있다. 우리가 보고자 하는건 기본형 데이터이다. 즉, 5, 7, 'abc', 'abcdef' 가 바뀔 수 없는 불변이기 때문에 불변값이라고 하는 것이다.

가변값

그렇다면 참조형은 모두 가변값일까? 기본적인 성질은 가변값인 경우가 많지만 설정에 따라 변경 불가능한 경우도 있고 아예 불변값으로 활용하는 방안도 있다. 이건 나중에 알아보도록 하고 아래 코드를 보도록 하자.

예시1. 객체(object)

let obj1 = {
  a: 1,
  b: 'bbb',
};

기본형 데이터와의 차이는 객체의 변수(프로퍼티)영역이 별도로 존재한다는 점이다. 또한 데이터 영역에 저장된 값은 모두 불변값이며 변수영역에는 다른 값을 얼마든지 대입할 수 있다. 바로 이 부분 때문에 흔히 참조형 데이터는 가변값이다 라고 말할 수 있는 것이다.

예시2. 객체 프로퍼티 변경

만약 아래처럼 obj1 객체에서 a값을 2로 바꿔주면 어떤일이 일어날까?

let obj1 = {
  a: 1,
  b: 'bbb',
};
obj1.a = 2;

2라는 값은 데이터 영역에 없으므로 새로 할당될 것이며, a는 새로 할당된 주소를 가리키도록 바뀔 것이다. 하지만 obj1 객체 자체는 근본적으로 변하지 않은셈이니 가변값이라고 할 수 있겠다. 즉, 새로운 객체가 만들어진것이 아니라 기존의 객체 내부의 값만 바뀐것이 된다.

예시3. 중첩 객체

좀 더 복잡한 경우를 보도록 하겠다. 객체안에 Array라는 객체가 중첩되어있는 경우 이를 중첩 객체라고 한다.

let obj = {
  x: 3,
  arr: [3, 4, 5] // ✅
};

이 경우 조금 복잡해지는데 핵심은 배열에 대한 변수영역이 하나 더 생성되었을 뿐이다. 역시나 배열에 저장될 데이터는 데이터영역이 가지고 있으며 변수영역은 데이터영역의 주소를 가지고 있다.

예시4. 중첩객체 변경

let obj = {
  x: 3,
  arr: [3, 4, 5]
};

obj.arr = 'str';

arr가 가진 값이 새로 생성된 str 데이터영역의 주소를 가리키도록 바뀌게 될 것이다. 그럼 쓸모없어진 배열 변수영역은 어떻게 될까? 가비지 컬렉터(GC) 에 의해 자동으로 수거대상이 된다.


변수 복사

기본형 데이터와 참조형 데이터의 차이는 변수 복사 후 값을 변경할 때 그 차이를 명확히 알 수 있다.

변수 복사 비교

let a = 10;
let b = a;

let obj1 = { c: 10, d: 'ddd' };
let obj2 = obj1;

변수를 복사하는 과정은 기본형 데이터와 참조형 데이터 둘 다 모두 변수영역이 서로 같은 데이터 영역 주소를 바라보게 되는 점에서 동일하다.

복사 후 프로퍼티 변경

b = 15;
obj2.c = 20;

둘의 차이는 여기서 확연하게 드러난다. 바로 변수 a와 b는 서로 다른 주소를 바라보게 되었고, 변수 obj1과 obj2는 여전히 같은 객체를 바라보고 있는 상태가 되기 때문이다.

a !== b // true
obj1 == obj2 // true

대부분의 자바스크립트 책에서는 기본형은 값을 복사하고 참조형은 주솟값을 복사한다고 설명하지만 엄밀히 말하면 기본형도 결국 주소값을 참조하기 때문에 자바스크립트의 모든 데이터 타입은 참조형 데이터일 수밖에 없다.

다만 기본형은 주솟값을 복사하는 과정이 한번이고, 참조형은 한 단계 더 거친다는 차이가 있는 것이다.

복사 후 객체 자체를 변경

b = 15;
obj2 = { c: 20, d: 'ddd' };

만약 위에처럼 객체 자세를 변경해버리면 어떻게 될까? 🤔 이는 새 객체를 할당한 것이기 때문에 obj1과 obj2도 완전 달라져버리게 된다. (obj1 !== obj2 성립)

즉, 참조형 데이터가 가변값이라고 설명할 때는 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립하게 된다.


References

profile
배움을 좋아합니다. 새로운 것을 좋아합니다.

0개의 댓글

관련 채용 정보