obj + obj2
같은 객체간의 덧셈이나 뺄셈 같은 연산이 수행되면, 객체는 원시값으로
변환되고, 이후의 연산이 수행된다.
true
를 반환한다.alert(obj)
같이 객체를 출력하려고 할 떄, 형 변환 됩니다.ToPrimitive
메서드를 사용하면 객체를 원시형으로 형 변환 할 수 있습니다.
객체의 형 변환은 3종류로 구분 되는데, 구분의 되는 기준을 hint
라고 합니다.
객체가 어떤 연산을 통해서 원시형으로 형 변환하는데, 형 변환되는 자료형을 hint
라고 부릅니다.
쉽게 말해 산술적인 연산을 하면 hint
는 숫자형이 되고, alert
같이 문자열을 출력
하면 hint
는 문자형이 됩니다. 연산의 결과가 숫자인지 문자인지 확실하지 않은 경우
hint
는 default
가 됩니다.
🔔 힌트란?
객체가 형 변환 하려는 자료형정도로 이해 합시다. 자세한 것은 명세서 참조 바랍니다.
🔔 객체의 형 변환 3종류
string(문자형)
number(숫자형)
default(알 수 없음)
String
alert()
함수 같이 객체를 문자형으로 변환하는 연산을 수행하는 경우
hint
는 String
이 됩니다. 아래 예제는 hint
가 String
이 되는 경우 입니다.
alert(obj); // 이 함수에 인자값은 어떤 자료형이든 출력 시, 문자열로 형 변환한다.
obj2[obj] = 123; // obj 객체를 키로 사용한다.
/* */
obj2[obj] = 123;
1) ob2 객체에 프로퍼티 키로 obj 객체를 할당한다.
2) 객체명이 할당되서, 객체명으로 접근할 수 있음 obj[obj]
let obj2 = {
[obj]: 123
};
Number
객체를 숫자형으로 형 변환하는 수학연산을 수행할 때 hint
는 number
가 된다.
아래 예제는 hint
가 Number
가 되는 경우 입니다.
let num = Number(obj); // obj 객체를 숫자형으로 형 뱐환
let n = +obj; // obj 객체를 숫자형으로 형 변환
let delta = date - date2; // date 객체와 date2 객체간의 뺼셈 연산 > 숫자형 형 변환
let greater = user1 > user2 // 객체간의 크기 비교 > 숫자형 형 변환
Default
연산 수행을 통해 객체가 어떤 자료형으로 변할 지 확실하지 않은 경우 hint
는 defeualt
가 됩니다. 예를 들어 이항 덧셈 연산자에서 +
은 피연산자의 자료형에 따라서 문자열을
합칠 수도 있고, 숫자를 더해주는 연산을 할 수도 있다. 어떤 자료형이 결과가 될지 확실치
않으므로 hint
는 default
가 된다.
또는, 동등 연산자를 사용해서 객체-문자형, 객체-숫자형, 객체-심볼형끼리 비교할 때도 객체를
어떤 자료형으로 바꿔야 할지 확실치 않으므로 hint
는 default
가 됩니다.
let total = obj1 + obj2; // 이항 덧셈 연산자에서 `hint`는 `default`가 된다
if ( user == 1 ) { ... }; // 동등 연산자는 hint로 default를 사용
🔔
>
<
같은 비교 연산자
크고 작음을 비교할 때 사용하는 비교 연산자는 피연산자로 문자형, 숫자형 둘 다
허용합니다. 어떤 자료형으로 비교할 지 확실치 않지만,hint
는number
로 고정 된다.
원래 같으면default
가 되는게 맞지만, 특수한 규칙으로 인해 객체를 비교할 때는
hint = number
가 됩니다.
이런 규칙들을 모두 외울 필요는 없습니다.Date
객체를 제외한 모든 내장 객체는
hint
가default 또는 number
인 경우 동일하게 처리하기 때문입니다. 우리도
커스텀한 객체를 만들 때 이 규칙을 따르면 됩니다.
⚠️ 논리형
hint
는 없다.
모든 객체는true
로 평가되기 때문에,hint
가 논리형인 경우는 없습니다.
내장 객체에서 사용하는 규칙처럼hint
가default
라면number
과 동일하게 처리하면
됩니다. 결국엔number
와string
두 종류의 형 변환만 남게 되죠.
JS
에서는 객체가 형 변환하려고 할 때, 아래와 같은 알고리즘에 따라서 메서드를 호출합니다.
객체에 obj[symbol.toPrimitive](hint)
메서드를 호출합니다. Symbol.toPrimitive
는 시스템 심볼로, 심볼형 키를 사용합니다.
symbol.
메서드가 없고, hint
가 String
이라면 obj.toString()
을 호출하고
없다면 obj.valueOf()
를 호출합니다.
symbol.
메서드가 없고, hint
가 number 또는 default
라면 obj.valueOf()
을
호출하고 없다면 obj.toString()
를 호출합니다.
모든 메서드가 존재하지 않는다면 자동으로 형 변환 할 수 없다.
✅ 호출한다고 없는 메서드를 호출하는 것이 아니라, 존재하는 메서드만 호출합니다.
Symbol.toPrimitive
Symbol.toPrimitive
은 시스템 심볼이며, 이 심볼은 목표로 하는 자료형(hint)을 명명
하는데 사용합니다.
/* 구문 */
obj[Symbol.toPrimitive] = function(hint) {
// 반드시 원시값을 반환해야 한다.
// hint는 string, number, default 중 하나가 될 수 있다.
// function 키워드는 생략될 수 있다.
}
설명으로는 이해하기 부족합니다. 실제 예시를 살펴보겠습니다.
let user = {
name: "John",
money: 50000,
[Symbol.toPrimitive](hint) { // 여기서 hint라는 키는 다른 이름으로 변경해도 됩니다.
alert(`hint: $[hint]`); // 현재 출력되는 hint를 보여줍니다.
return ( hint == 'string' ) ? this.name : this.money;
// hint가 문자열이면 John, 아니라면 50000 출력, default인 경우도 50000 출력
},
};
alert(user); // hint: string -> John 출력
alert(+user); // hint: number -> 50000 출력
alert(user + 1); // hint: default -> 50001 출력
/* */
alert(user)
1. alert는 ( ) 괄호안의 값이 어떤 자료형이던 문자열로 형 변환 한다
2. alert로 인해 객체가 목표로 하는 자료형(hint)가 문자형이 된다.
3. 객체가 원시형(문자)으로 형 변환 하기 위해 Symbol.toPrimitive 메서드를 호출한다.
4. toPrimitive() 인자 값으로는, `hint(String)`가 전달 됨
> this.name > John이 출력 된다.
alert(+user);
1. 단항 연산자로 인해, 객체가 목표로 하는 자료형(hint)가 숫자형이 된다.
2. 객체가 원시형(문자)으로 형 변환 하기 위해 Symbol.toPrimitive 메서드를 호출한다.
3. toPrimitive() 인자 값으로 `hint(Number)`를 전달
> this.money > 50000이 출력 된다.
alert(user + 1)
1. 이항 연산자는 객체가 어떤 자료형을 목표로 하는 지 알 수 없음 > `hint`는 `default`
2. 객체가 원시형(문자)으로 형 변환 하기 위해 Symbol.toPrimitive 메서드를 호출한다.
3. toPrimitive() 인자 값으로 `hint(default)`를 전달 > default는 Number와 동일하게
작동하게 만들었음. > this.money > 50000 출력
4. 50000 + 1 = 50001이 된다.
/* */
`hint`는 객체가 어떤 자료형을 목표로 형 변환 하는지에 대한 값이 들어 있다.
[Symbol.toPrimitive]() 메서드의 인자 값으로 받는 `hint`는 암시적으로 정해놓은 것이므로
(hint를 'hard')라고 변경해도 됨, 다만 이름 자체는 의미 있게 사용하는 것이 좋습니다.
이렇게 구현하면, user
객체는 hint
에 따라서 문자열로 또는 숫자형으로 변환 합니다. [Symbol.toPrimitive)(hint)
메서드 하나로, 객체에 대한 형 변환을 다룰 수 있습니다.
toString
valueOf
는 심볼이 생기기 이전부터 존재한 평범한 메서드 입니다.
이 메서드를 이용하면 형 변환을 직접 구현할 수 있습니다.
JS
는, 객체에 Symbol.Primitive()
가 없다면 toString
이나 valueOf
를 호출합니다.
생성된 메서드는, 반드시 원시값을 반환해야 한다. toString
이나 valueOf
가 객체를 반환하면
그 결과는 무시됩니다. 메서드가 처음부터 없었던 것이 됩니다.
✅ 호출에는 순서가 있다.
✔️hint
가string
인 경우
toString > valueOf (toString
이 없다면valueOf
호출 )
✔️ 그 외의 경우
valueOf > toString (valueOf
가 없다면toString
호출 )
일반 객체는 기본적으로 toString
과 valueOf
에 적용되는 규칙을 따릅니다.
✅
toString
과valueOf
에 적용되는 규칙
toString
은 문자열"[object Object]"
를 반환한다. > 객체의 프로퍼티가 숨겨짐
valueOf
는 객체 자신을 반환한다.
hint
가 String
인 경우아래 예제는 객체를 원시형으로 형 변환 해주는 메서드가 없는 경우입니다.
이 경우, 상위 객체로 부터 hint
에 맞는 메서드를 자동 호출하게 됩니다.
let user = { name: "John" };
alert(user); // {object Object}
alert(user.valueOf() === user); // true, valueOf()는 객체 자신을 반환하기 때문에
// 메서드를 호출한 것 자체가 없어진다. 즉 user === user를 비교하게 되는 것
/* */
alert(user);
1. alert로 인해 객체가 목표로 하는 자료형이 String이 된다. ( hint = string )
2. Symbol.toPrimitive 메서드가 있는지 확인 > 없다
3. toString()을 메서드 탐색 > 없다 | valueOf() 메서드 탐색 > 없다
4. user 라는 객체에는 toString() 메서드가 없기에, 자신의 상위 객체(object)를 찾아서
toString() 메서드를 상속받아 옵니다. 즉 자동으로 호출되고 사라진다.
5. 결론은 toString()으로 인해 {object Object}가 출력 됩니다.
❗ hint가 숫자형이나 다른 자료형이였다면 valueOf()메서드가 호출됩니다.
⚠️ 오해하지 말기
user
객체의 프로퍼티에 name이 문자열 값이라 호출되는게 아닙니다. 저 값은 아무런
의미가 없습니다. 오직hint
만 확인합니다.
이번에는 toString()
과 valueOf()
를 사용해서 객체를 직접 형 변환 해보겠습니다.
let user = {
name: "John",
money: 10000,
// hint가 string인 경우
toString() {
return '내 이름은:' + this.name;
},
// hint가 string이 아닌 경우 ( number or default )
valueOf() {
return this.money;
},
};
alert(user); // hint: string > 내 이름은: John 출력
alert(+user); // hint: number > 10000 출력
alert(user + 10000); // hint: default > 20000 출력
/* */
`Symbol.toPrimitive` 메서드를 사용한 예제와 동일하게 작동합니다.
모든 형 변환을 한 곳에서 처리해야 하는 경우도 있습니다. 이런 경우에는 toString()
만
구현해주면 됩니다. 객체에 Symbol.toPrimitive
또는 valueOf
메서드가 없는 경우엔
toString
메서드가 모든 형 변환을 문자열로 변환시킵니다.
let user = {
name: "John",
toString() {
return this.name;
}
};
alert(user); // toString -> John
alert(user + 500); // toString -> John500
객체를 형 변환 시키는 3개의 메서드는, hint
에 명시된 자료형으로의 형 변환을 보장해주지
않습니다. toString()
이 항상 문자열을 반환한다는 보장이 없고, Symbol.toPrimitive
의
hint
가 number
인 경우에 항상 숫자형을 반환한다는 보장도 없습니다.
확실한 것은, 객체를 원시형의 값으로 반환해 준다는 것 입니다.
피연산자가 산술적인 연산을 수행하면, 자료형이 숫자형으로 변환 됩니다.
*
곱셈 연산 또한 산술 연산이므로 피연산자가 숫자형으로 형 변환 합니다.
이 규칙은 객체도 포함 됩니다.
*
연산하면, 객체가 원시형으로 변환 됩니다.let obj = {
// 다른 메서드 valueOf등이 없다면, toString이 모든 자료형을 문자열로 형 변환한다.
toString() {
return "2";
}
};
alert( obj * 2 ); // 4
/* */
alert( obj * 2 );
1. obj 객체의 `hint`는 `default` > valueOf나 Symbol 메서드가 없다 > toString() 호출
2. 문자열 2가 반환되어 연산 수행 > "2" * 2 > 곱셈 연산은 피연산자를 숫자형으로 변환시킴
3. 2 * 2 = 4가 된다.
이항 연산자에서 피연산자를 덧셈 연산하는 과정중에 피연산자가 문자열이라면 덧셈 대신
문자열 병합이 발생합니다. 객체도 이 규칙에 해당 됩니다.
let obj = {
toString() {
return "2";
},
};
alert( obj + 2 ); // 문자열 "22" 출력
/* */
alert( obj + 2 );
1. obj 객체의 `hint`는 `default` > 다른 메서드가 없기에 toString() 호출
2. 문자열 "2"를 반환 후 연산 수행 > "2" + 2 > 문자열에서 이항 덧셈 연산자는 병합이 일어남
3. "2" + "2" > "22"가 된다.
원시 값을 기대하는 내장 함수나(alert) 연산자를 사용할 때 객체는 원시형으로 자동 형 변환됨.
객체가 원시형으로 형 변환 될 때, 기준이 되는 값은 hint
라고 부른다.
🔔 객체 -> 원시형으로 형 변환 3가지 방식 ( 3종류의
hint
)
✔️ String
alert
함수 같이 문자열을 필요로 하는 연산
✔️ Number
수학 연산이 이루어 질 때 ( 이항 덧셈 연산자 제외 )
✔️ Default
연산의 결과가 어떤 자료형일지 모르는 경우 ( 숫자형+문자형 등등 )
🔔 객체 -> 원시형으로 형 변환 알고리즘
1. 객체가 원시값으로 형 변환 되려고 하면 먼저Symbol.toPrimitive()
메서드를 객체
내부에서 찾는다. 있다면 호출하고 없으면 다음 단계 수행
2.hint
의 값이String
인 경우 객체 내부에서toString()
메서드를 찾는다. 있다면
호출하고, 없다면valueof()
메서드를 호출
3.hint
의 값이Number || Default
인 경우 객체 내부에서valueOf()
메서드를 찾는다.
있다면 호출하고, 없다면toString()
메서드를 호출
⚠️ 형 변환이 되는 메서드가 객체 내부에서 존재하지 않는다면,hint
의 값에 알맞은
메서드를 상위 객체로부터 상속받아 온다. 문자형이라면toString()
숫자형이나 알 수
없는 경우에는valueOf()
를 상속 받아 오는지는 확실하지 않다.
( 추후에 수정 예정 or 댓글로 알려주세요 )
obj.toString()
만 사용해도 '모든 변환’을 다룰 수 있기 때문에, 실무에선 obj.toString()
만
구현해도 충분한 경우가 많습니다. 반환 값도 ‘사람이 읽고 이해할 수 있는’ 형식이기 때문에
실용성 측면에서 다른 메서드에 뒤처지지 않습니다. obj.toString()
은 로깅이나 디버깅
목적으로도 자주 사용됩니다.