Search
Duplicate

Symbol 오브젝트

1. primitive 값, wrapper 오브젝트

primitive 값

기존에 여러 키워드들에 공부하고 포스팅할때를 생각해보면 대다수가 자동으로 오브젝트를 통해 설정되었습니다. 하지만, Primitive값은 오브젝트가 아니라 값이며 함수를 가지고있지도 않습니다.
예를들어 const num = 100; 을 실행하면 num이라는 변수에는 100만 할당되며 그외에 어떤것도 할당되지 않습니다. 100 자체가 num의 primitive 값입니다.
ES5에서는 primitive 값 타입이 string, number, boolean, null, undefined 이 있습니다.
ES6에서는 symbol 타입이 추가되었습니다.
const num = 100;
JavaScript
복사

wrapper 오브젝트

wrapper 오브젝트란 Primitive 값이 포함된 오브젝트로 Primitive값과는다르게 메소드가 있습니다.
우리가 많이 사용하던 오브젝트로 대표적으로는
string: String
number: Number
boolean: Boolean
symbol: Symbol
오브젝트들이 있습니다.
const obj = new String(100);
JavaScript
복사
[[
100이라는 primitive값을 설정해줘야하는데 new String(100)으로 값이 아닌 오브젝트로 설정할 때는 obj 라는 String 오브젝트의 인스턴스의 [[PrimitiveValue]에 100이 설정됩니다.
더하여 undefined, null은 wrapper오브젝트가 없기 때문에 값으로만 사용할 수 있습니다.
이렇게 Wrapper 오브젝트를 이용하면 내부의 메소드들이 연결되어있고 [[PrimitiveValue]] 가설정되어 사용할 수 있습니다. 그런데 내부 형태가 조금 다른 wrapper 오브젝트가 있는데 바로 Symbol입니다.
const sym = Symbol("ABC");
JavaScript
복사
위에서 소개한 String 오브젝트에서는 내부의 __proto__와 [[PrimitiveValue]]를 통해 연결된 메소드와 값을 확인할 수 있었습니다. 하지만 Symbol 오브젝트는 스코프를 펼칠 수 없으며, 내부의 메소드나 [[PrimitiveValue]]역시 표시되지 않습니다.
하지만, 그렇다고 Primitive 값이 없는 것은 아닌데, 이렇게 보여지는 이유는 Symbol은 Primitive 값을 외부에 노출시키지 않는 특성이 있고 이 특성이 무엇보다 우선시되기 때문입니다. 그리고 그렇기 때문에 노출이 안됨으로써 생기는 손해역시 감수하겠다는게 Symbol의 트레이드오프입니다.
대괄호 두개로 감싼 프로퍼티들은 엔진 내부에서 사용되는 프로퍼티입니다.

2. Symbol() 함수

Symbol()함수는 값을 생성하여 반환하는데 반환된 값을 볼 수는 없습니다.
const sym = Symbol(); console.log(sym);//Symbol() console.log(typeof sym);//symbol
JavaScript
복사
⇒ 새로운 값을 생성해 반환(Symbol())하기 때문에 값을 설정한다기보다는 생성한다는 표현이 적절합니다. 이는 생성한 값을 외부에 노출시키지 않으려는 의도로 볼 수 있습니다.
그리고 Symbol은 new 연산자도 사용할 수가없는데 이는 프로그램 전체에서 유일한 값을 제공한다는 의도입니다.
const one = Symbol("catsbi"); const two = Symbol("catsbi"); console.log(one === two);//false
JavaScript
복사
둘 다 출력을 해보면 Symbol(catsbi) 를 출력하지만 실제로는 프로그램 전체에서 하나만 있는 값을 생성하기 때문에 둘을 비교하면 false가 출력됩니다.
이렇게 외부노출에 폐쇄적이라는 말은 Symbol 값으로 연산을 하거나 타입을 변경하는것도 불가능하다는 말이 됩니다.
let sym = Symbol(); try{ const add = sym + 5; } catch(e){ console.log("연산 불가"); };
JavaScript
복사
sym은 값이지만 연산이 불가능합니다.
let sym = Symbol(); try{ +sym; } catch{ console.log("타입변경 불가"); };
JavaScript
복사
단항연산자 +를 통해 Number로 타입을 바꾸려 시도하지만 예외가 발생합니다.
이는 외부에 값이 노출되지 않게 하기 위해서입니다.
Symbol의 파라미터로는 그래서 보통 주석, 설명을 작성합니다.
const sym = Symbol("이곳은 catsbi의 notion 포스팅영역입니다. "); console.log(sym);
JavaScript
복사
[실행 결과]
Symbol(이곳은 catsbi의 notion 포스팅영역입니다. )
생성한 Symbol의 값을 볼 수 없기 때문에 파라미터에는 값에 대한 설명이 필요할 때 사용합니다. 이는 Symbol() 실행에 영향을 주지는 않으며 출력을 하더라도 Symbol값이 출력되는게 아닌 Symbol값을 생성한 코드만 표시됩니다.
Symbol값을 문자열로 바꿔 연결을 하고자한다면, 가능은합니다. 하지만 제한적이고 내부값을 연결할 수는 없습니다.
const sym = Symbol("설명"); console.log(sym.toString() + "연결");//Symbol(설명)연결 try{ const str = new String(sym); } catch{ console.log("에러 발생"); }
JavaScript
복사
1.
console.log(sym.toString() + "연결")
⇒ toString()메소드를 호출하면 에러가 발생하지는 않지만 내부값이 연결되는게 아닌 값이 변환되지 않고 값을 만든형태(Symbol(설명))에 문자열을 연결합니다.
2.
new String(sym);
⇒ Symbol()은 타입 변환이 불가능하기 때문에 new String(sym)형태는 에러가 발생합니다.
마지막으로 Symbol은 Template 에 사용이 불가능 합니다.
const sym = Symbol("이곳은 catsbi의 notion 포스팅영역입니다. "); try{ `${sym}` }catch{ console.log("`${sym} 불가`"); };
JavaScript
복사
⇒ 여기서 Template안에 Symbol()을 넣으면 에러가 발생하는데, 이는 Teamplate에서 sym내부의 값을 꺼내서 파싱하려할 때 에러가 발생하기 때문입니다.

3. Symbol 사용 형태

사용 형태

1.
Object의 프로퍼티 키로 사용
: Symbol은 프로젝트 전체에서 유일한 값을 가집니다. 그렇기에 중복되지않습니다. 그래서 이를 symbol-keyed property라고도 부릅니다.
const pointSym = Symbol("가격"); const obj = {[pointSym]: 100}; console.log(obj[pointSym]);//100 console.log(obj.pointSym);//undefined
JavaScript
복사
⇒ Symbol값을 Object의 프로퍼티 키로 작성해 사용할 수 있는데 이를 symbol-keyed property라 부릅니다.
⇒ 하지만, 주의점도있는데 Symbol 프로퍼티는 객체 그래프 탐색으로 접근할 수 없으며 대괄호를 통해 접근해야 합니다. 그래서 obj.pointSym은 undefined가 출력됩니다.
2.
Object에서 함수 이름으로 사용
: 사용법 자체는 프로퍼티 키로 사용하는것과 유사합니다.
const plusSym = Symbol("덧셈함수"); const obj = { [plusSym](param){ return param; } }; console.log(obj[plusSym](200));//200
JavaScript
복사
⇒ 사실 함수 이름도 실행 컨텍스트의 환경레코드에는 프로퍼티(key): 값(value)로 저장되기 때문에 식별자 해결을 통해 값(함수)를 호출하는데 Symbol이기때문에 객체 그래프 탐색(.)이 아닌 대괄호를 통해 호출한다는 점이 주의점입니다.
3.
for-in문에서 사용
: Symbol의 내부 프로퍼티중 [[Enumerable]]은 false입니다. 그렇기에 Symbol은 열거되지 않습니다. (Object.getOwnPropertySymbols()로 가능하다.)
const obj = { [Symbol("100")]: 100, two: 200 }; for (let key in obj){ console.log(key); };
JavaScript
복사
[실행 결과]
two
4.
for-of문에서 사용
: 배열 안에 Symbol()작성을 해서 순회는 가능합니다.
const list = [Symbol("1"), Symbol("2")]; for(let value of list){ console.log(value); };
JavaScript
복사
[실행 결과]
Symbol(1)
Symbol(2)
5.
JSON.stringify()에서 사용
: Symbol값은 문자열로 변환되지 않습니다. stringify()를 통해 직렬화를 할 때 Symbol()의 값이 노출되기 때문에 변환을 시도하면 Symbol은 변환에서 제외됩니다.
const sym = Symbol("JSON"); const result = JSON.stringify({[sym]: "ABC"}); console.log(result);//{}
JavaScript
복사