원시 데이터 타입(Primitive data types)
컴퓨터 프로그래밍 언어에서 기본적으로 제공되는 데이터 타입
JS에서 원시 데이터 값은 오브젝트가 아니라 '값'이며 함수를 갖고 있지 않다.
원시 데이터 타입으로는 숫자형, 문자형, 불리언, null, undefined, 그리고 심볼이 있다.
참고로 null, undefined는 래퍼 객체가 없다. 그래서 값으로만 쓰이는 것이다.
원시 데이터 타입은 변수에 직접 값을 할당하거나 함수의 매개변수로 전달할 수 있다.
또 값 복사에 의해 전달되며, 변경 불가능해서 값을 직접 변경할 수 없다.
원시 데이터 타입은 메모리의 스택에 저장된다.
예컨대 아래 코드를 보면 num 변수에 100만 할당되었다.
이 100을 원시값이라고 한다.
const num = 100;
래퍼 오브젝트 (Wrapper Object)
원시 데이터 값을 감싸는 형태의 객체
원시 데이터의 메서드 및 속성에 접근할 수 있도록 한다.
const num = 42; // 숫자형 원시 값
const str = 'Hello'; // 문자열 원시 값
const numObj = new Number(num); // 숫자형 원시 값에 대한 Number 객체 생성
const strObj = new String(str); // 문자열 원시 값에 대한 String 객체 생성
console.log(numObj.toFixed(2)); // 숫자형 원시 값에 대한 toFixed() 메서드 호출
console.log(strObj.length); // 문자열 원시 값에 대한 length 속성 접근
맨 처음 num과 str는 원시 데이터 타입인 숫자형과 문자형으로 생성되었다.
이후 numObj, strObj는 각각 new 키워드로 Number, String과 같은 래퍼 객체의 생성자 함수를 호출해 원시 데이터를 감싸는 객체를 생성했다. 실제로 타입을 찍어보니 둘다 오브젝트였고, 그 중 strObj만 들여다 보았다.
PrimitiveValue, 처음 선언했던 'Hello'가 원시 값으로 설정되었음을 알 수 있었다.
[[]] 이렇게 쓰이는 것은 JS 엔진이 설정한 프로퍼티라는 뜻이라고 한다.
그래서 래퍼 객체를 왜, 어떻게 쓰는 걸까?
- 객체 기능 제공
- 원시 데이터 타입이 메서드나 속성을 가지지 않기 때문에 래퍼 객체를 사용하면 메서드 호출이나 속성에 접근할 수 있다. (다만 이 부분은 그냥 num.toFixed(2) 이렇게 쓰는 게 더 편하고 성능적으로도 효율적이다.)
- 객체로 전달 가능
- JS는 객체를 사용하는 게 일반적이다. 원시 데이터를 래퍼 객체로 감싸면 원시 데이터를 객체 형태로 전달할 수 있다. (어디다 쓸지는 모르겠지만)
일반적으로 원시 값에 직접 접근하는 것이 권장된다고 한다.
심볼 이야기 하려다가 여기까지 오게 된 이유는... 심볼은 이 래퍼 객체를 갖고 있음에도 안 보인다.
Symbol()
값을 생성하며 반환하지만 반환된 값을 볼 수 없다.
- 파라미터: 설명, 주석(opt)
- 반환: 프로그램 전체 내 유일한 심볼 값
- 심볼의 가장 큰 특징은 값이 외부에 노출되지 않는 것이다.
- new 연산자로 인스턴스를 만들 수 없다.
- 심볼 값으로 연산을 하거나, 심볼 타입을 바꿀 수 없다.
아래와 같이 sym을 출력할 때 생성한 값이 출력되지 않고, 값을 생성한 코드만 표시된다.
const sym = Symbol();
console.log(sym) // Symbol()
console.log(typeof sym) // symbol
// 심볼 id에는 "id"라는 설명이 붙습니다.
let id = Symbol("id");
위 코드와 같이 파라미터로 설명을 붙일 수 있는데 디버깅 시에 유용하다.
또 프로그램 전체에서 유일한 값을 생성한다.
심볼은 유일성이 보장되는 자료형이라 설명이 동일한 심볼을 여러 개 만들어도 같지 않다.
const one = Symbol();
const two = Symbol();
console.log(one === two); // false
let id1 = Symbol("id");
let id2 = Symbol("id");
console.log(id1) // Symbol(id)
console.log(id2) // Symbol(id)
console.log(id1 === id2) // false
전달한 파라미터, 즉 설명만 같을 뿐 심볼 값은 달라 false가 반환되는 것이다.
심볼은 값을 볼 수 없기 때문에, 이렇게 설명/주석을 문자열로 작성해두면 값을 확인하기 쉽다.
위에서 말했던 것처럼 심볼은 값을 외부에 노출하지 않으려 한다.
따라서 계산도 할 수 없고, 타입도 변경할 수 없다.
let sym = Symbol();
try {
const add = sym + 5
} catch(e) {
console.log("연산 불가")}
// 연산 불가
let sym = Symbol();
try {
+sym;
} catch {
console.log("값 타입 변경 불가")}
// 값 타입 변경 불가
Symbol 사용
그래서 이렇게 값을 꽁꽁 숨기고, 유일성을 갖는 심볼을 어디다 쓸까?
심볼 값은 유일하기 때문에 중복되지 않아 문자열과 함께 객체의 프로퍼티 키로 사용된다.
이러한 것을 symbol-keyed property라고 한다.
상수 sym에는 Symbol()의 결과를 할당했다. 이 sym을 프로퍼티의 키로 사용했다.
const sym = Symbol("설명");
const obj = {[sym]: 100};
console.log(obj[sym]) // 100
console.log(obj.sym) // undefined
함수 이름으로도 사용할 수 있다.
const sym = Symbol("함수 이름");
const obj = {
[sym](param) {
return param;
}
};
console.log(obj[sym](200)) // 200
심볼은 for... in 구문에서 사용할 수 없다.
왜냐하면 열거할 수 없기 때문이다. ([[Enumerable]]: false)
아래 코드를 실행시키면 심볼 부분은 건너뛰고 two만 반환한다.
const obj = {
[Symbol("100")]: 100,
two: 200
};
for (let key in obj) {
console.log(key) // two
}
대신 for... of 구문에서 사용할 수 있다.
for... of 구문은 객체도 사용하듯 이터러블 객체면 사용할 수 있다.
단 전개는 되지만 심볼 값이 아니라 심볼을 작성한 코드가 그대로 나오게 된다.
const list = [Symbol("100")];
for (let value of list) {
console.log(value); // Symbol(100)
};
JSON.stringify()를 쓰면 오브젝트의 프로퍼티 키와 값이 {"key": "value"} 처럼 문자열로 변환된다.
하지만 역시 심볼에서는 이러한 변환을 할 수 없고, 빈 오브젝트가 반환된다.
const sym = Symbol("JSON");
const result = JSON.stringify({[sym]: "ABC"});
console.log(result) // {}
**참고
'👋🏻 JavaScript > 📖 자바스크립트 ES6+' 카테고리의 다른 글
[JS] 콜백 함수와 동기/비동기를 알아보자 (0) | 2023.07.22 |
---|---|
[JS] yield와 제너레이터 오브젝트의 메소드 return(), throw()를 알아보자 (0) | 2023.06.23 |
[JS] Generator 오브젝트를 알아보자 : Generator 함수, Generator Function, yield, next() (0) | 2023.06.23 |
[JS] Math 오브젝트를 알아보자 (0) | 2023.06.22 |
[JS] Array 오브젝트의 이터레이터 오브젝트 생성을 알아보자 (0) | 2023.06.22 |