4.8 객체를 원시형으로 변환하기
obj1 + obj2처럼 객체끼리 연산을 하는 경우에 객체->원시값으로 자동형변환 발생 후 연산이 수행됨
1. 객체는 논리 평가 시 true를 반환, 예외x, 따라서 객체는 숫자형이나 문자형으로만 형 변환 발생
2. 숫자형으로의 형 변환은 객체끼리 빼는 연산을 할 때나 수학 관련 함수를 적용할 때 발생
ex) 객체 Date끼리 차감할 때 (date1 - date2) 두 날짜의 시간차이가 반환됨
3. 문자형으로의 형 변환은 대게 alert(obj)같이 객체를 출력할 때 발생
- ToPrimitive
특수 객체 메서드를 사용하여 형변환을 숫자형or문자형 중 원하는대로 조절가능
객체 형 변환은 세 종류로 구분되는데 'hint'(=목표로 하는 자료형)라 불리는 값이 구분 기준
"string" : alert함수같이 문자열을 기대하는 연산 수행시(객체-문자형 변환), hint = string
// 객체를 출력하려고 함
alert(obj);
// 객체를 프로퍼티 키로 사용하고 있음
anotherObj[obj] = 123;
"number" : 숫자 연산을 적용할 때(객체-숫자형 변환), hint = number
// 명시적 형 변환
let num = Number(obj);
// (이항 덧셈 연산을 제외한) 수학 연산
let n = +obj; // 단항 덧셈 연산
let delta = date1 - date2;
// 크고 작음 비교하기
let greater = user1 > user2;
"default" : 연산자가 기대하는 자료형이 '확실치 않을 때', hint = default
ex) 이항 덧셈 연산자 + 는 문자열을 합치거나 숫자를 더해주거나 피연산자의 자료형에 따라 다른 경우, hint = default
ex) 동등 연산자 == 를 사용해 객체-문자형, 객체-숫자형, 객체-심볼형끼리 비교할 때, 객체를 어떤 자료형으로 바꿔야할지 확실치않을 때
// 이항 덧셈 연산은 hint로 `default`를 사용합니다.
let total = obj1 + obj2;
// obj == number 연산은 hint로 `default`를 사용합니다.
if (user == 1) { ... };
+ 크고작음을 비교하는 <, > 도 피연산자에 문자형과 숫자형 모두 허용하나 hint = number로 고정, 하위 호환성때문에 정해진 규칙
+ Date 객체를 제외한 모든 내장 객체는 hint가 default인 경우와 number인 경우를 동일하게 처리
! boolean hint는 없음
모든 객체는 true로 평가됨, 내장 객체에 사용되는 규칙처럼 default와 number를 동일하게 처리하면 두 종류(객체-문자형, 객체-숫자형)만 남게됨
JS가 형변환이 필요할 때 해당 알고리즘으로 원하는 메서드를 찾고 호출
1. 객체에 obj[Symbol.toPrimitive](hint)메서드가 있는지 찾고 호출, Symbol.toPrimitive는 시스템 심볼로 심볼형 키로 사용
2. 1에 해당하지않고 hint=string일 경우 obj.toString(), obj.valueOf() 호출 (존재하는 메서드만 실행됨)
3. 1,2에 해당하지않고 hint=number 또는 hint=default라면 obj.valueOf()나 obj.toString() 호출(존재하는 메서드만 실행됨)
- Symbol.toPrimitive
JS의 내장 심볼인 Symbol.toPrimitive, 목표로 하는 자료형(hint)를 명명하는데 사용
obj[Symbol.toPrimitive] = function(hint) {
// 반드시 원시값을 반환해야 합니다.
// hint는 "string", "number", "default" 중 하나가 될 수 있습니다.
};
//user객체에 객체-원시형 변환 메서드obj[Symbol.toPrimitive](hint) 구현
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
'평범한' 메서드인 toString과 valueOf로 형변환 직접 구현 가능
객체에 Symbol.toPrimitive가 없는 경우 JS는 규칙에 따라 toString, valueOf를 호출함
- hint가 string인 경우, toString -> valueOf 순으로 호출
- 그 외, valueOf -> toString 순
이 메서드들은 반드시 원시값반환, toString이나 valueOf가 객체를 반환하면 그 결과는 메서드가 처음부터 없었던 것처럼 무시됨,
일반 객체는 기본적으로 toStrimg과 valueOf에 적용되는 규칙을 따름
- toString은 문자열 "[object Object]"를 반환
- valueOf는 객체 자신을 반환
let user = {name: "John"};
alert(user); // [object Object]
alert(user.valueOf() === user); // true
valueOf는 객체 자신을 반환하기 때문에 그 결과가 무시되므로 사용할 경우가 없음
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를 사용한 경우와 동일하게 동작
질문??? +user???
모든 형 변환을 한 곳에서 처리해야 하는 경우
let user = {
name: "John",
toString() {
return this.name;
}
};
alert(user); // toString -> John
alert(user + 500); // toString -> John500
객체에 Symbol.toPrimitive와 valueOf가 없으면 toStrimg이 모든 형 변환 처리
- 반환 타입
Symbol.toPrimitive, toString, valuOf의 메서드는 'hint'에 명ㅇ시된 자료형으로의 형 변환을 보장하지는 않지만 객체가 아닌 원시값을 반환
! 과거의 잔재
toString이나 valueOf가 객체를 반환해도 에러가 발생하지 않지만 반환값이 무시되고 메서드 자체가 존재하지 않은 것처럼 동작,
Symbol.toPrimitive는 무조건 원시자료를 반환해야함
- 추가 형 변환
객체가 피연산자일 때 변환과정
1. 객체는 원시형으로 변화
2. 변환 후 원시값이 원하는 형이 아닐 경우 또 다시 형변환 발생
let obj = {
// 다른 메서드가 없으면 toString에서 모든 형 변환을 처리합니다.
toString() {
return "2";
}
};
alert(obj * 2); // 4, 객체가 문자열 "2"로 바뀌고, 곱셈 연산 과정에서 문자열 "2"는 숫자 2로 변경됩니다.
이항 덧셈 연산은 문자열을 연결함
let obj = {
toString() {
return "2";
}
};
alert(obj + 2); // 22("2" + 2), 문자열이 반환되기 때문에 문자열끼리의 병합이 일어났습니다.
요약
원시값을 기대하는 내장함수나 연산자를 사용할 때 객체-원시형으로의 자동 형변환
객체-원시형으로의 형 변환은 hint를 기준으로 세종류로 구분
- "string" : alert 같이 문자열을 필요로 하는 연산
- "number" : 수학 연산
- "default" : 드물게 발생 _ 내장 객체는 대게 hint가 default일 때와 number 일때 동일하게 처리하므로 아주 드물게 발생
객체-원시형 변환 알고리즘
1. 객체에 obj[Symbol.toPrimitive](hint) 메서드가 있는지 찾고, 있다면 호출
2. 1에 해당하지 않고 hint가 "string"이라면 obj.toString()이나 obj.valueOf()를 호출
3. 1, 2에 해당하지 않고 hint가 "number"나 "default"라면 obj.valueOf()나 obj.toString()을 호출
obj.toString()만 사용해도 '모든 변환'을 다룰 수 있기 때문에 실무에선 obj.toString()만으로 충분한 경우가 대다수, 반환 값도 '사람이 읽고 이해할 수 있는' 형식이므로 실용성 측면에서 다른 메서드에 뒤쳐지지않으므로 로깅이나 디버깅 목적으로도 자주 사용
'JS > [web] 코어 JS 튜토리얼' 카테고리의 다른 글
[코어 JS] 5.2 - 숫자형 (0) | 2021.10.18 |
---|---|
[코어 JS] 5.1 - 원시값의 메서드 (0) | 2021.10.12 |
[코어 JS] 4.7 - 심볼형 (0) | 2021.10.11 |
[코어 JS] 4.6 - 옵셔널 체이닝 '?.' (0) | 2021.10.11 |
[코어 JS] 4.5 - 객체: new 연산자와 생성자 함수 (0) | 2021.10.11 |