Primitive값은 오브젝트가 아니라 값이며 함수를 가지고 있지도 않습니다. 예를 들어 const num = 100; 을 실행하면 num이라는 변수에는 100만 할당되며 그 외 아무것도 첨부되지 않습니다. 100이 num의 primitive값 입니다.
ES5에서는 primitive 값 타입이 string, number, boolean, null, undefined이 있습니다. ES6에서 symbol 타입이 추가되었습니다.
wrapper 오브젝트란 Primitive값이 포함된 오브젝트로 Primitive값과는 다르게 메소드가 있습니다.
const stringObj = new String(100); const stringValue = 200; const numberObj = new Number(123); const numberValue = 456; const sym = Symbol("ABC");
Symbol()은 값을 생성하여 반환하는데, 반환된 값은 노출되지 않아 확인할 수 없습니다. 다른 값에 중복되지 않는 유일무이한 값으로 따라서, 주로 충돌 위험이 없는 유일한 프로퍼티 키를 만들기 위해 사용합니다.
const sym = Symbol(); console.log(sym); console.log(typeof sym); // Symbol() // symbol
새로운 값을 생성해 반환하기 때문에 값을 설정하기보다는 생성한다는 표현이 적절합니다. Symbol 값이 출력되지 않고, 생성한 코드 형태가 표시됩니다. 이는 생성한 값을 외부에 노출시키지 않으려는 의도로 볼 수 있습니다.
const one = Symbol("mySymbol"); const two = Symbol("mySymbol"); console.log(one === two); // false
둘 다 출력을 해보면 Symbol(mySymbol)을 출력하지만 실제로는 프로그램 전체에서 하나만 있는 값을 생성하기 때문에 둘을 비교하면 false가 출력됩니다.
이렇게 외부노출에 폐쇄적이라는 말은 Symbol 값으로 연산을 하거나 타입을 변경하는것도 불가능하다는 말이 됩니다.
Symbol 값으로 연산불가
let sym = Symbol(); try { const add = sym + 5; } catch(e) { console.log("연산 불가"); }; // 연산 불가
Symbol 값 타입변경 불가
let sym = Symbol(); try { +sym; } catch { console.log("타입변경 불가"); }; // 타입변경 불가
단항연산자 +를 통해 Number로 타입을 바꾸려 시도하지만 예외가 발생합니다. 이는 외부에 값이 노출되지 않게 하기 위해서입니다.
파라미터에 보통 주석, 설명을 작성
const sym = Symbol("주석, 설명"); console.log(sym); // Symbol(주석, 설명)
생성한 Symbol 값을 볼 수 없으므로 값 설명이 필요할 때 사용합니다. Symbol 실행에 영향을 미치지 않습니다.
Symbol 값을 문자열로 변환하여 연결
const sym = Symbol("설명"); console.log(sym.toString() + "연결"); try { const str = new String(sym); } catch { console.log("에러 발생"); } // Symbol(설명)연결
에러가 발생하지 않지만, 값은 변환되지 않고 값을 만든 코드형태에 문자열을 연결합니다. new String(sym) 형태는 에러가 발생합니다.
Symbol은 Template에 사용 불가
const sym = Symbol("주석, 설명"); try { `${sym}` } catch { console.log("`${sym} 불가`"); }; // `${sym} 불가`
여기서 Template안에 Symbol()을 넣으면 에러가 발생하는데, 이는 Teamplate에서 sym내부의 값을 꺼내서 파싱하려할 때 에러가 발생하기 때문입니다.
Object의 프로퍼티 키로 사용
Symbol은 프로젝트 전체에서 유일한 값을 가집니다. 그렇기에 중복되지 않습니다.
const pointSym = Symbol("가격"); const obj = {[pointSym]: 100}; console.log(obj[pointSym]); console.log(obj.pointSym); // 100 // undefined
⇒ Symbol값을 Object의 프로퍼티 키로 작성해 사용할 수 있는데 이를 symbol-keyed property라 부릅니다.
⇒ 주의할 사항은 Symbol 프로퍼티는 객체 그래프 탐색(.)으로 접근할 수 없으며, 대괄호를 통해 접근해야 합니다.
⇒ obj.pointSym은 undefined가 출력됩니다.
Object에서 함수 이름으로 사용
사용법 자체는 프로퍼티 키로 사용하는것과 유사합니다.
const plusSym = Symbol("plus100"); const obj = { [plusSym](param) { return param + 100; } }; console.log(obj[plusSym](200)); // 300
⇒ 함수 이름도 실행 콘텍스트의 환경레코드에는 프로퍼티(key): 값(value)로 저장되기 때문에 식별자 해결을 통해 값(함수)를 호출한다는 점에서 유사합니다.
for-in문에서 사용
Symbol의 내부 프로퍼티 중 [[Enumerable]]은 false입니다. 그렇기에 Symbol은 열거되지 않습니다. Object.getOwnPropertySymbols()로 심볼만 열거하는 것으로 보완합니다.
const hundred = Symbol("100"); const obj = { [hundred] : 100, two: 200 }; for (let key in obj) { console.log(key); }; // two
for-of문에서 사용
배열 안에 Symbol()를 작성할 경우, 순회는 가능합니다.
const list = [Symbol("1"), Symbol("2")]; for(let value of list) { console.log(value); }; // Symbol(1) // Symbol(2)
JSON.stringify()에서 사용
JSON.stringify()는 object의 프로퍼티 키와 값을 {"key": "value"} 형태로 변환합니다. stringify()를 통해 직렬화를 할 때 Symbol()의 값이 노출되기 때문에, 변환을 시도하면 Symbol은 변환에서 제외됩니다. Symbol값은 문자열로 변환되지 않습니다.
const sym = Symbol("JSON"); const result = JSON.stringify({[sym]: "ABC"}); console.log(result); // {}