자바스크립트/개념

자바스크립트의 Symbol.toPrimitive 란?

Jake Seo 2023. 1. 1. 11:30

Symbol.toPrimitive 란?

  • 잘 알려진 심볼 중 하나이다.
  • 강제 타입변환 시에 타입변환 힌트를 받아들이고, 어떤 원시 타입을 반환할지에 대한 메서드를 정의할 수 있다.
  • 객체를 primitive value 로 변경하는 강력한 방법을 제공한다.
    • JS 에서는 다양한 경우에 값을 강제로 변환한다.
    • ex) +object 인 경우 숫자로 강제 변환한다. String(object) 인 경우엔 문자열로 변환한다.
    • 내부적으로 hint 라는 인자를 받게 된다.
    • number, string, default 중 하나를 받아 선호하는 타입이 무엇인지 체크할 수 있다.
  • 모든 타입 변환 알고리즘에서 우선순위를 갖는다.
    • toString() 이나 valueOf() 보다 앞선 우선순위를 갖는다.
      • toString() 은 해당 타입을 문자열로 변환할 때 사용한다.
      • valueOf() 는 해당 타입을 오브젝트로 변환할 때 사용한다.

객체의 [Symbol.toPrimitive] 오버라이드 해보기

const normalObject = {};

console.log(+normalObject); // NaN
console.log(`${normalObject}`); // [object Object]
console.log("" + normalObject); // [object Object]

const object1 = {
  // hint 를 이용한 로직을 작성할 수 있다.
  [Symbol.toPrimitive](hint) {
    console.log(`hint was ${hint}`);

    if (hint === "number") {
      return 42;
    }

    if (hint === "string") {
      return "forty two";
    }

    return "default";
  },
};

// hint: number
console.log(+object1); // 42

// hint: string
// 템플릿 리터럴 내의 오브젝트는 String(object1); 을 한 것과 동일하다.
// 타입을 primitive string 으로 바꿔준다.
console.log(`${object1}`); // "forty two"

// hint: default
// 더하기 연산자는 문자열과 숫자 모두 가능하다.
console.log("" + object1); // "default"

타입 강제변환 시 들어온 힌트에 따라 적절한 형태의 primitive type 으로 변환할 수 있다. 용도 자체가 primitive type 으로 변환시키기 위한 접근자 프로퍼티여서, primitive type 이 아닌 다른 타입으로 반환하면, 아래와 같은 에러를 만날 수 있다.

Uncaught TypeError: Cannot convert object to primitive value

직접 호출한다면?

const object1 = {
  [Symbol.toPrimitive](hint) {
    console.log(`hint was ${hint}`);

    if (hint === "number") {
      return 42;
    }

    if (hint === "string") {
      return "forty two";
    }

    return "default";
  },
};

console.log(object1[Symbol.toPrimitive]());
// hint was undefined
// default

보통 직접 호출할 일은 없지만, 직접 호출하면 힌트로는 undefined 가 적용된다.

기본 정의 살펴보기

[Symbol.toPrimitive](hint) {
  let methods = hint === "string"
          ? ["toString", "valueOf"]
          : ["valueOf", "toString"];

  for (const methodName of methods) {
    const method = this[methodName];

    if(typeof method === "function") {
      const result = method.call(this);
      if(result === null || typeof result !== "object") {
        return result;
      }
    }
  }

  throw new TypeError();
};

힌트가 string 으로 들어오면, toString() 이 함수로 정의되어 있는지 먼저 확인하고 호출해본다. 이후 결과 값이 적절한지 판단 후 반환한다. stirng 이 아니라면, valueOf() 를 우선순위로 두고 같은 동작을 한다.

만일 적절한 메서드가 없다면 TypeError 를 던진다.

DatetoPrimitive 동작 살펴보기

const date = new Date();
console.log(String(date)); // hint: string, Tue Jan 31 2023 00:03:19 GMT+0900 (한국 표준시)
console.log(+date); // hint: number, 1675090999694
console.log("" + date); // hint: default,  Tue Jan 31 2023 00:03:19 GMT+0900 (한국 표준시)
console.log(date[Symbol.toPrimitive]()); // hint: undefined, Uncaught TypeError: Invalid hint: undefined
  • default 일 때와 string 일 때가 같다.
  • undefined 인 경우 에러가 난다.
반응형