[JS] #12 Symbol 오브젝트

primitive 값

Primitive값은 오브젝트가 아니라 값이며 함수를 가지고 있지도 않습니다. 예를 들어 const num = 100; 을 실행하면 num이라는 변수에는 100만 할당되며 그 외 아무것도 첨부되지 않습니다. 100이 num의 primitive값 입니다.

ES5에서는 primitive 값 타입이 string, number, boolean, null, undefined이 있습니다. ES6에서 symbol 타입이 추가되었습니다.


wrapper 오브젝트

wrapper 오브젝트란 Primitive값이 포함된 오브젝트로 Primitive값과는 다르게 메소드가 있습니다.

  • wrapper 오브젝트가 있는 프리미티브 값 타입
    • string: String
    • number: Number
    • boolean: Boolean
    • symbol: Symbol - 외부에 노출되지 않는 특성
  • undefined, null은 wrapper 오브젝트가 없기 때문에 값으로만 사용할 수 있습니다.
  const stringObj = new String(100);
  const stringValue = 200;
  const numberObj = new Number(123);
  const numberValue = 456;
  const sym = Symbol("ABC");

  • 빌트인 오브젝트로 설정할 때는 만들어진 인스턴스의 [[PrimitiveValue]]에 설정됩니다.
  • numberObj, stringObj가 wrapper 오브젝트입니다.
  • sym은 펼칠 수가 없으며, [[PrimitiveValue]]가 표시되지 않습니다.
  • 하지만, 실제로 Primitive값이 없는 것은 아니며 외부에 노출시키지 않는 특성 때문입니다.
  • Symbol 오브젝트는 new 연산자로 인스턴스를 만드는 것도 이러한 특성에 의해서 불가능합니다.

Symbol() 함수

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내부의 값을 꺼내서 파싱하려할 때 에러가 발생하기 때문입니다.


Symbol 사용 형태

  1. 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가 출력됩니다.

  2. Object에서 함수 이름으로 사용
    사용법 자체는 프로퍼티 키로 사용하는것과 유사합니다.

    const plusSym = Symbol("plus100");
    const obj = {
      [plusSym](param) {
        return param + 100;
      }
    };
    console.log(obj[plusSym](200));
    // 300

    ⇒ 함수 이름도 실행 콘텍스트의 환경레코드에는 프로퍼티(key): 값(value)로 저장되기 때문에 식별자 해결을 통해 값(함수)를 호출한다는 점에서 유사합니다.

  3. 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
  4. for-of문에서 사용
    배열 안에 Symbol()를 작성할 경우, 순회는 가능합니다.

    const list = [Symbol("1"), Symbol("2")];
    for(let value of list) {
      console.log(value);
    };
    // Symbol(1)
    // Symbol(2)
  5. JSON.stringify()에서 사용
    JSON.stringify()는 object의 프로퍼티 키와 값을 {"key": "value"} 형태로 변환합니다. stringify()를 통해 직렬화를 할 때 Symbol()의 값이 노출되기 때문에, 변환을 시도하면 Symbol은 변환에서 제외됩니다. Symbol값은 문자열로 변환되지 않습니다.

    const sym = Symbol("JSON");
    const result = JSON.stringify({[sym]: "ABC"});
    console.log(result);
    // {}

좋은 웹페이지 즐겨찾기