
자바스크립트의 변수로 선언하는 모든 값들은 종류에 따라 각각의 데이터 타입을 가지고 있습니다.
console.log(23) // 23
console.log('23') // 23
위 코드에서 23과 '23'을 출력하게 되면 23로 동일하게 나오지만 이 둘은 전혀 다른 값이며, 확보해야할 메모리 크기도 다드고, 사용하는 연산도 다르게 됩니다.
이처럼 값들은 눈으로는 비슷해 보여도 데이터 타입에 따라 차이와 특징을 가지고 있습니다. 현재 ES6 자바스크립트에서는 데이터 타입이 원시타입과 객체타입으로 나뉘며, 총 7개의 타입을 제공하고 있습니다.
| 구분 | 데이터 타입 | 설명 |
|---|---|---|
| 원시 타입 | 숫자 타입 (number) | 정수와 실수를 포함한 숫자 |
| 문자열 타입 (string) | 문자열 | |
| 불리언 타입 (boolean) | 논리적 참(true) / 거짓 (false) | |
| undefined 타입 | 변수를 선언했지만 값을 할당하지 않은 경우의 초기값 | |
| null 타입 | 값이 없다는 것을 명시적으로 나타내는 값 | |
| 심벌 타입 (symbol) | 변경 불가능한 원시 타입 값 | |
| 객체 타입 | 객체, 함수, 배열 등의 타입 |
원시타입은 숫자, 문자열, 불리언, undefined, null, 심벌(symbol)이 포함되며, 이 값들은 모두 변경 불가능한 값(immutable value) 입니다. 원시 타입의 값을 변수에 할당하면, 그 변수는 해당 값 자체를 메모리에 저장하게 됩니다. 즉, 변수는 값을 직접적으로 가리키고, 그 값은 변하지 않습니다.
자바스크립트에서는 정수와 실수를 구분하지 않고 하나의 숫자 타입만 제공되며, 모든 숫자는실수(소수점 이하가 있는 숫자)로만 처리됩니다.
let num1 = 23; // 정수
let num2 = -23; // 음의 정수
let num3 = 23.23; // 실수
위와 같이 정수, 음의 정수, 실수 모두 숫자 타입이며, 위에서 말했듯 모든 수는 실수로 처리가 됩니다. 즉, 정수로 표현된 숫자도 내부적으로는 실수로 저장되고 처리되며, 정수끼리 나눌 때도 결과는 실수가 나올 수 있습니다.
console.log(23 === 23.0); // true
console.log(3 / 3); // 1
console.log(3 / 2); // 1.5
추가적으로 숫자 타입은은 무한대와 NaN(not-a-number)도 표현할 수 있습니다.
console.log(23 / 0); // Infinity
console.log(23 / -0); // -Infinity
console.log(23 * 'number'); // NaN
위처럼, 양의 무한대와 음의 무한대는 각각 0 또는 -0으로 나누었을 때 발생하며, NaN은 숫자가 아닌 값과 연산할 경우 나타납니다.
숫자 타입은 기본적으로 실수로 처리되므로, 소수점 없는 정수 값으로 사용하고 싶을 때는 다음과 같은 방법을 사용할 수 있습니다.
let num = 23.9;
// Math.floor() - 소수점 이하를 내림하여 정수 반환
console.log(Math.floor(num)); // 23
// Math.round() - 소수점 이하를 반올림 하여 정수 반환
console.log(Math.round(num)); // 24
// Math.ceil() - 소수점 이하를 올림하여 정수 반환
console.log(Math.ceil(num)); // 24
// parseInt() - 문자열로 된 숫자나 실수 값을 정수 부분만 반환
console.log(parseInt(num)); // 23
문자열 타입은 텍스트 데이터를 나타내는데 사용되며 작은 따옴표(''), 큰 따옴표(""), 백틱(``)으로 텍스트를 감싸서 표현할 수 있습니다.
let str1 = '문자1';
let str2 = "문자2";
let str3 = `문자3`;
문자열을 따옴표로 감싸지 않으면 자바스크립트 엔진은 이를 변수나 키워드로 인식하여 오류를 발생시키기 때문에 문자열타입임을 명확하게 표현하기 위해서는 문자열을 반드시 따옴표로 감싸야 합니다.
let str4 = name // ReferenceError: name is not defined
불리언 타입은 논리적 참(true), 거짓(false)을 나타내는 데이터 타입입니다.
let test1 = true
let test2 = false
불리언 값은 주로 조건문에서 사용되어, 특정 조건이 참인지 거짓인지 판단하는 데 활용됩니다.
예를 들어, if문과 같은 제어문에서 조건을 평가할 때 불리언 값이 중요한 역할을 합니다.
let test1 = true
if (test1) {
console.log('참!'); // 참!
} else {
console.log('거짓!');
}
undefined 타입의 값은 변수를 초기화 할때 사용하는 값인 undefined를 나타냅니다.
var name;
console.log(name); // undefined
var 키워드를 사용하여 변수를 선언한 후, 값을 할당하지 않으면 undefined가 출력됩니다. 이는 변수를 선언하는 것은 했지만 아직 사용 가능한 값이 없음을 의미합니다.
null은 변수에 값이 없다는 것을 의도적으로 명시 할떄 사용됩니다. 즉, null을 할당하면 그 변수는 아무것도 없다는 의미를 가지게 됩니다.
let name = 'jin';
console.log(name) // jin
name = null;
console.log(name) // null
위의 코드에서 처음에 name 변수에 'jin'을 할당한 후, 나중에 null을 재할당하게 되면, 기존에 참조하던 'jin'은 더 이상 참조되지 않고, name은 이제 아무것도 참조하지 않게 됩니다. 이처럼 특정 변수의 값이 의도적으로 없음을 표현할때는 null 타입을 사용합니다.
심벌(Symbol) 타입은 ES6에서 추가된 7번째 데이터 타입으로, 다른 값과 중복되지 않는 유일무이한 값을 생성합니다. 주로 객체의 유일한 키를 만들 때 사용되며, 이는 충돌할 위험이 없는 속성을 정의하는 데 유용합니다.
const uniqueKey = Symbol('key'); // 심벌 생성
const obj = {
[uniqueKey]: '이 값은 심벌 키로 접근됩니다.' // 심벌을 키로 사용하는 객체
};
console.log(obj[uniqueKey]); // 이 값은 심벌 키로 접근됩니다.
위의 코드에서 Symbol('key')을 사용하여 생성한 uniqueKey는 유일한 값이며, 이를 객체의 키로 사용하면 다른 모든 키와 충돌하지 않습니다. 심벌은 주로 객체의 비공식적인 속성을 정의하거나, 라이브러리나 API에서 고유한 값을 전달할 때 유용하게 사용됩니다.
추가로, 심벌은 불변(immutable)이며, 동일한 설명을 가진 두 개의 심벌도 서로 다르기 때문에 항상 유일한 값으로 취급됩니다. 이는 코드를 더욱 안전하게 만들고, 의도하지 않은 속성 충돌을 방지하는 데 도움이 됩니다.
객체 타입은 객체, 함수, 배열등 원시타입을 제외한 데이터 구조를 포함하며, 해당 값들은 변수 할당 시 실제 값이 아닌 참조 값을 저장 하게 됩니다. 그래서 객체타입은 변경 가능한 값(mutable value) 이며, 참조 타입이라고도 불립니다.
let person = {
name: 'Jin',
age: 29,
isStudent: false,
};
console.log(person.name); // Jin
console.log(person['age']); // 29
위 코드에서 person 객체는 name, age, isStudent라는 속성을 가지며, 이들 속성은 언제든지 수정할 수 있습니다. 객체의 변경 가능성 덕분에 프로퍼티를 추가하거나 제거하는 것이 가능합니다.
person.age = 30; // age 속성 수정
person.gender = 'male'; // 새로운 속성 추가
delete person.isStudent; // 속성 삭제
console.log(person);
// { name: 'Jin', age: 31, gender: 'male' }
변수를 객체로 할당하면, 그 변수는 객체가 저장된 메모리 주소를 참조합니다. 이로 인해 원시 타입과는 달리 변수의 값을 직접적으로 가리키지 않으며, 같은 객체를 참조하는 여러 변수가 있을 경우, 한 변수에서 객체의 속성을 변경하면 다른 변수에서도 그 변경 사항이 반영됩니다.
let anotherPerson = person;
anotherPerson.name = 'Kim';
console.log(person.name); // Kim (원본 객체 변경)
이렇게 변경된 내용이 person 객체에 영향을 주는 이유는, 두 변수가 동일한 객체를 참조하고 있기 때문입니다.
원시타입과 객체타입의 가장 큰 차이인 변경이 불가능하고, 변경이 가능하다는건 어떤 의미일까요?
원시값을 변수에 할당하게 되면 변수에는 실제 값이 저장되게 됩니다. 반면 객체를 변수에 할당하면 변수에는 참조 값이 저장되게 됩니다.
원시 타입의 값(변경 불가능한 값)은 한 번 생성되면 읽기 전용으로 변경이 불가능합니다.
변수는 기본적으로 메모리 공간을 찾기 위해 붙인 이름이며, 값을 할당하게 되면 해당 메모리 주소에 값이 저장됩니다.
위 사진에서, age라는 변수에 30이라는 값을 할당하면, 30이라는 값이 저장된 메모리 공간의 주소를 참조하게 됩니다. 이후 age 변수의 값을 50으로 재할당하면, 50이라는 값이 저장된 새로운 메모리 주소를 참조하게 됩니다.
원시 타입은 이렇게 값을 한 번 할당한 후, 할당한 값의 메모리 주소가 계속 남아 있고, 재할당을 하게 되면 새로운 값을 만들어서 저장하고 참조하는 메모리 주소만 바꾸기 때문에 변경 불가능한 값, 불변성의 특성을 가지고 있다고 할 수 있습니다.
옷을 입는 상황을 생각해서 날이 추워져서 옷장에서 반팔 위에 체크 셔츠를 입고 집을 나왔다고 가정해 보겠습니다. 집에서 나왔을 때, 내가 입고 있는 상태는 체크 셔츠를 입고 있는 것입니다. 하지만 걷다가 더워져서 체크 셔츠를 벗게 되면, 내가 입고 있는 옷은 체크 셔츠에서 반팔로 변하게 됩니다. 이때 체크 셔츠는 손에 들고 있는 상태로 남아 있습니다.
즉, 옷은 갈아입었지만, 체크 셔츠는 여전히 그대로 남아 있는 것입니다.
변수 선언: 옷을 입는 행위 (체크 셔츠를 입음)
값 할당: 체크 셔츠 (변수에 값이 저장됨)
값의 재할당: 반팔 (체크 셔츠를 벗고 새로운 옷으로 변경)
객체 타입(참조 타입)의 값은 메모리 공간을 찾을 때 참조 값에 접근하는 방식으로 변경 가능합니다. 객체를 할당한 변수를 참조하면, 메모리에 저장된 참조 값을 통해 실제 객체에 접근할 수 있습니다.
변수는 객체를 참조하고 있다? 이것은 변수가 객체의 메모리 주소를 가리킨다는 의미입니다. 따라서 변수는 객체의 실제 값을 저장하는 것이 아니라, 해당 값이 저장된 메모리 주소를 바라보게 됩니다.
이러한 특성 덕분에 객체의 속성 값은 재할당 없이도 직접 변경할 수 있습니다. 위 사진에서, person이라는 변수에 객체 {name: 'jin'}을 할당하면, 이 객체를 저장한 메모리 주소가 person 변수에 저장됩니다. 만약 age라는 프로퍼티를 추가하여 person 변수를 수정하게 되면, 메모리 주소는 그대로 유지되면서 객체의 값만 변경되므로 객체는 변경이 가능합니다.
객체는 큰 가방과 같다 생각하면 어떤 느낌인지 쉽게 그림이 그려집니다. 가방 안에 물건을 넣고 빼도 가방의 존재는 변하지 않듯이, 객체의 속성을 추가하거나 제거해도 객체 자체는 동일하게 유지됩니다. 즉, 가방은 여전히 존재하지만 그 안에 무엇이 들어있는지는 언제든지 변경할 수 있습니다.
변수로 선언하는 데이터의 타입에 대해 정리해 보았습니다.
객체에 관련된 내용은 아직 정리할 부분이 훨씬 더 많은데 내용이 너무 길어져서 다음에 이어서 정리해보겠습니다~
출처:
모던 자바스크립트 Deep Dive 6장 데이터 타입 (59p ~ 73p)
모던 자바스크립트 Deep Dive 11장 원시 값과 객체의 비교 (137p ~ 153p)