자바스크립트는 동적 타입(dynamic typed) 언어 혹은 느슨한 타입(loosely typed) 언어이며 구문 작성이 자유롭고 느슨하다고 전 포스팅에서 한 번 언급한 적이 있다. 예를 들면 이런 식이다.
const a = 12;
const b = "12";
위와 같이 코드를 작성했을 때, 따로 변수에 자료형을 지정해주지 않았지만 자바스크립트는 자동으로 a는 number
타입으로, b는 string
타입으로 인식한다.
그리고 이런 것도 가능하다.
console.log(a == b); // true
console.log(a === b); // false
첫번째 줄에서는 a와 b를 비교할 때 먼저 형변환을 통해 두 값의 자료형을 일치시킨 뒤 비교하는 동등 연산자(==, equality operator)를 사용했기 때문에 a와 b가 같다는 결과가 나온다.
하지만 두번째 줄에서는 데이터 타입의 변환 없이 비교하여, 타입의 동등 여부까지 검사하는 일치 연산자(===, strict equality operator)를 사용했기 때문에 a와 b가 다르다는 결과가 나온다.
이처럼 Javascript는 다른 언어(C, Java 등)들에 비해 자료형 즉 타입에 비교적 너그럽다.
이런 Javascript의 특성은 코드를 짤 때는 행복하게 느껴질 수도 있지만, 코드에서 오류가 발생했을 땐 지옥의 디버깅을 겪게 하는 악마로 느껴질 수도 있다.
그러므로 자바스크립트 내부에서 변수들의 타입이 어떻게 다루어지는지 이해하고 Typescript를 이용한 정적 타이핑을 한다면 보다 더 슬기로운 코딩생활을 할 수 있겠지? 🙂
그래서 이번 포스팅에서는 슬기로운 코딩생활을 위해 자바스크립트의 데이터 타입에 대해 알아보려고 한다 🙌🏻
원시 타입에 속하는 데이터 타입에는 숫자(Number & Bigint), 문자열(String), 논리형(Boolean), null, undefined, 심볼(Symbol)이 있다.
참고로 NaN은 Not a Number라는 뜻으로 숫자에 속한다. null, undefined, NaN이 어떻게 다른지는 나중에 따로 포스팅해보도록 하겠다 !
자바스크립트에서 원시 타입은 변수에 할당될 때 메모리에 고정 크기로 원시 값을 저장하고 그 값을 변수가 직접 가리키는 형태를 띈다.
즉 데이터를 저장하기 위한 별도의 데이터 공간을 확보해 값을 저장하고, 그 공간을 변수 영역에 저장한다.
➡️ 변수 영역과 데이터 영역이 존재
이렇게 할당된 값은 불변성(immutable)을 갖는다.
즉 메모리에 할당된 원시 타입의 값은 그 자체가 변경될 수 없다.
이에 대해 조금 더 자세히 설명해 보자면, 변수에 값을 재할당할 때 기존 값이 변하는 것처럼 보이지만 사실은 새로운 메모리에 재할당한 값이 저장되고 변수가 가리키는 메모리가 달라지는 것이다.
위 사진을 통해, str에 "문자열"이라는 값을 할당한 후 "문자열2"라는 값을 재할당하면 새로운 메모리에 재할당한 값인 "문자열2"가 저장되고 변수가 그 메모리를 가리키게 되는 것을 알 수 있다.
원시 타입의 변수들은 데이터 복사가 일어날 때 값이 담긴 주솟값을 바로 복사한다 ✨
var str1 = "hello";
var str2 = str1;
str1 = "world";
console.log(str1); // world
console.log(str2); // hello
위의 코드를 보면 알 수 있듯 원본 변수에 값을 재할당해도 복사본의 값은 변하지 않는다.
이것은 새로운 변수 str2에 str1을 복사할 때 str1의 값이 저장되어 있는 메모리 공간을 변수가 가리키도록 하기 때문이다.
그리고 str1에 다른 값인 "world"를 재할당하면 메모리 공간에 "world"가 새로 할당되고, str1은 "world"가 저장되어 있는 공간을 가리키게 된다.
즉 원시 타입의 값은 한 번 만들면 바꿀 수 없다. 이미 만들어진 값은 가비지 컬렉팅을 당하지 않는 이상 사라지지 않는다 !
자바스크립트에서 원시 타입을 제외한 나머지는 참조 타입(객체, Object)이라 할 수 있다.
객체의 하위 분류에 속하는 데이터 타입에는 배열(Array), 함수(Function), 날짜(Date), 정규표현식(RegExp) 등이 있다.
참조 타입과 원시 타입의 가장 큰 차이점은, 객체의 변수(프로퍼티) 영역이 추가로 존재한다는 것이다.
예를 들어,
var obj1 = {
a: 1,
b: "bbb"
};
이렇게 생긴 객체를 선언 및 초기화할 때 생기는 일에 대해 알아보자.
위로부터 알 수 있듯 데이터 영역에 저장된 값들은 모두 불변값이지만, 객체가 별도로 할애한 영역은 "변수 영역"이고 여기에는 얼마든지 다른 값을 대입할 수 있다.
이 때문에 참조형 데이터는 불변하지 않는 값, 즉 가변값이라고 한다.
참조 타입의 변수들은 데이터 복사가 일어날 때 값이 담긴 주솟값을 바로 복사하지 않고, 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복사한다.
var obj1 = {
a: 1,
b: "bbb"
};
var obj2 = obj1;
obj2.a = 20;
console.log(obj1.a); // 20
위의 코드에서, obj2는 obj1이 가리키는 데이터 묶음의 주소를 복사한다. (같은 객체를 보고 있음)
그러므로 obj2에서 프로퍼티의 값을 변경하면, 같은 객체의 주소를 가지고 있는 obj1에서도 값이 변하게 되는 것이다.
하지만,
var obj1 = {
a: 1,
b: "bbb"
};
var obj2 = obj1;
obj2 = {
a: 20,
b: "cc"
};
console.log(obj1.a); // 1
이처럼 복사 후 obj2에 새로운 객체를 할당해 주게 되면 메모리 데이터 영역의 새 공간에 새로운 객체가 저장되고 그 주소를 변수 영역의 obj2에 저장하게 된다!
즉 obj1과 obj2가 다른 객체를 가리키게 되므로 obj1이 가리키는 값은 달라지지 않는다.
이로부터 알 수 있는 점은, 참조형 데이터를 가변값이라 할 때의 "가변"은 참조형 데이터 자체를 변경할 때(두 번째 경우)가 아니라 그 내부의 프로퍼티를 변경할 때(첫 번째 경우)에만 성립한다는 것이다.
코어 자바스크립트(위키북스, 정재남)
[Java Script] 원시타입과 참조타입 👀