반응형

hint

객체 형 변환은 세 종류로 구분되는데, 'hint'라 불리는 값이 구분 기준이 된다.

hint는 목표로 하는 자료형 정도로 이해하면 된다.

// 객체를 출력하려고 함
alert(obj);

// 객체를 프로퍼티 키로 사용하고 있음
anotherObj[obj] = 123;
  • alert 함수같이 문자열을 기대하는 연산을 수행할 때는(객체-문자형 변환),hint가 string이 된다.

 

// 명시적 형 변환
let num = Number(obj);

// (이항 덧셈 연산을 제외한) 수학 연산
let n = +obj; // 단항 덧셈 연산
let delta = date1 - date2;

// 크고 작음 비교하기
let greater = user1 > user2;
  • 수학 연산을 적용하려 할 때(객체-숫자형 변환),hint는 number가 된다.
  • 크고 작음을 비교할 때 쓰이는 연산자 <, > 역시 피연산자에 문자형과 숫자형 둘 다를 허용하는데,
    이 연산자들은 hint를 number로 고정한다. (하위 호환성 때문에 정해진 규칙)

 

// 이항 덧셈 연산은 hint로 `default`를 사용합니다.
let total = obj1 + obj2;

// obj == number 연산은 hint로 `default`를 사용합니다.
if (user == 1) { ... };
  • 연산자가 기대하는 자료형이 확실치 않을 때 hint는 default가 된다.(아주 드물게 발생)

 

boolean hint는 없다

boolean hint는 존재하지 않는다. 모든 객체는 그냥 true로 평가된다.

Date 객체를 제외한 모든 내장 객체는 hint가 default인 경우와 number인 경우를 동일하게 처리한다.

그렇게되면 결국엔 두 종류의 형 변환(객체-문자형, 객체-숫자형)만 남게된다.

 

Symbol.toPrimitive

자바스크립트엔 Symbol.toPrimitive 라는 내장 심볼이 존재하는데,

이 심볼은 아래와 같이 목표로 하는 자료형(hint)을 명명하는데 사용된다.

obj[Symbol.toPrimitive] = function(hint) {
	// 반드시 원시값을 반환해야 합니다.
	// hint는 "string", "number", "default" 중 하나가 될 수 있습니다.
};

 

let user = {
	name: "John",
	money: 1000,

    [Symbol.toPrimitive](hint) {
            alert(`hint: ${hint}`);
            return hint == "string" ? `{name: "${this.name}"}` : this.money;
	}
};

// 데모:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
  • user는 hint에 따라 (자기 자신을 설명해주는) 문자열로 반환되기도 하고 숫자로 변환되기도 한다.
  • user[Symbol.toPrimitive]를 사용하면 메서드 하나로 모든 종류의 형 변환을 다룰 수 있다.

 

toString과 valueOf

객체에 Symbol.toPrimitive가 없으면 자바스크립트는 toString이나 valueOf를 호출한다.

일반 객체는 기본적으로 toString과 valueOf에 적용되는 다음 규칙을 따른다.

let user = {name: "John"};

alert(user); // [object Object]
alert(user.valueOf() === user); // true
  • toString은 문자열 "[object Object]"을 반환합니다.
  • valueOf는 객체 자신을 반환합니다. 
과거의 잔재
toString이나 valueOf가 객체를 반환해도 에러가 발생하지 않습니다. 
다만 이때는 반환 값이 무시되고, 메서드 자체가 존재하지 않았던 것처럼 동작합니다. 
이렇게 동작하는 이유는 과거 자바스크립트엔 에러라는 개념이 잘 정립되어있지 않았기 때문입니다.
반면에 Symbol.toPrimitive는 무조건 원시자료를 반환해야 합니다. 그렇지 않으면 에러가 발생합니다.

 

let user = {
	name: "John",
	money: 1000,

    // hint가 "string"인 경우
    toString() {
    	return `{name: "${this.name}"}`;
    },

    // hint가 "number"나 "default"인 경우
    valueOf() {
    	return this.money;
    }

};

alert(user); // toString -> {name: "John"}
alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500
  • Symbol.toPrimitive를 사용한 위쪽 예시와 동일하게 동작합니다.
  • hint가 'string’인 경우: toString -> valueOf 순(toString이 있다면 toString을 호출, toString이 없다면 valueOf를 호출함)
  • 그 외: valueOf -> toString 

 

let user = {
    name: "John",

    toString() {
    	return this.name;
    }
};

alert(user); // toString -> John
alert(user + 500); // toString -> John500
  • 객체에 Symbol.toPrimitive와 valueOf가 없으면, toString이 모든 형 변환을 처리합니다.

 

반환 타입

toString()이 항상 문자열을 반환하리라는 보장이 없고, Symbol.toPrimitive의 hint가 "number"일 때

항상 숫자형 자료가 반환되리라는 보장이 없습니다.
확신할 수 있는 단 한 가지는 객체가 아닌 원시값을 반환해 준다는 것뿐입니다.

 

추가 형 변환

객체가 피연산자일 때는 다음과 같은 단계를 거쳐 형 변환이 일어난다.

  1. 객체는 원시형으로 변화된다. 변환 규칙은 위에서 설명
  2. 변환 후 원시값이 원하는 형이 아닌 경우엔 또다시 형 변환이 일어난다.
let obj = {
    // 다른 메서드가 없으면 toString에서 모든 형 변환을 처리합니다.
    toString() {
    	return "2";
    }
};

alert(obj * 2); // 4, 객체가 문자열 "2"로 바뀌고, 
곱셈 연산 과정에서 문자열 "2"는 숫자 2로 변경됩니다.

 

⭐ 요약


원시값을 기대하는 내장 함수나 연산자를 사용할 때 객체-원시형으로 형 변환이 자동으로 일어난다.

객체-원시형으로 형 변환은 hint를 기준으로 세 종류로 구분할 수 있다.

"string" (alert 같이 문자열을 필요로 하는 연산)
"number" (수학 연산)
"default" (드물게 발생함)
  • 내장 객체는 대개 hint가 default일 때와 number일 때를 동일하게 처리한다.
  • 따라서 실무에선 hint가 default인 경우와 number인 경우를 합쳐서 처리하는 경우가 많다.

 

객체-원시형 변환엔 다음 알고리즘이 적용된다.

객체에 obj[Symbol.toPrimitive](hint)메서드가 있는지 찾고, 있다면 호출합니다.
1에 해당하지 않고 hint가 "string"이라면,
obj.toString()이나 obj.valueOf()를 호출합니다.
1과 2에 해당하지 않고, hint가 "number"나 "default"라면
obj.valueOf()나 obj.toString()을 호출합니다.

 

obj.toString()만 사용해도 '모든 변환'을 다 다룰 수 있기 때문에, 실무에선obj.toString()만 구현해도 충분한 경우가 많다.

obj.toString()은 로깅이나 디버깅 목적으로도 자주 사용된다.

 

반응형