객체에 obj1 + obj2
추가하거나 obj1 - obj2
빼거나 alert(obj)
사용하여 인쇄하면 어떻게 되나요?
JavaScript에서는 연산자가 객체에 대해 작동하는 방식을 사용자 정의할 수 없습니다. Ruby 또는 C++와 같은 일부 다른 프로그래밍 언어와 달리 추가(또는 다른 연산자)를 처리하기 위한 특수 객체 메서드를 구현할 수 없습니다.
이러한 작업의 경우 객체는 기본 요소로 자동 변환된 다음 이러한 기본 요소에 대해 작업이 수행되어 기본 값이 생성됩니다.
이는 중요한 제한 사항입니다. obj1 + obj2
(또는 다른 수학 연산)의 결과는 다른 객체가 될 수 없습니다!
예를 들어 벡터나 행렬(또는 성과 등)을 나타내는 개체를 만들고 이를 추가한 다음 결과로 "합계된" 개체를 기대할 수 없습니다. 이러한 건축적 업적은 자동으로 "기존에서 벗어나" 있습니다.
따라서 여기서는 기술적으로 많은 일을 할 수 없기 때문에 실제 프로젝트에서는 개체에 대한 수학적인 계산이 없습니다. 드문 경우를 제외하고 이런 일이 발생하면 코딩 실수로 인해 발생합니다.
이 장에서는 객체가 기본 객체로 변환되는 방법과 객체를 사용자 정의하는 방법을 다룹니다.
우리는 두 가지 목적을 가지고 있습니다:
Date
개체). 우리는 나중에 그것들을 만나게 될 것입니다.유형 변환 장에서 우리는 기본 요소의 숫자, 문자열 및 부울 변환에 대한 규칙을 살펴보았습니다. 그러나 우리는 사물에 대한 공백을 남겨 두었습니다. 이제 메소드와 기호에 대해 알고 있으므로 이를 채우는 것이 가능해졌습니다.
true
입니다. 숫자 및 문자열 변환만 존재합니다.Date
객체(날짜 및 시간 장에서 다룰)를 뺄 수 있으며 date1 - date2
의 결과는 두 날짜 간의 시간 차이입니다.alert(obj)
사용하여 객체를 출력할 때 발생합니다.특수 객체 메소드를 사용하여 문자열 및 숫자 변환을 직접 구현할 수 있습니다.
이제 기술적인 세부 사항을 살펴보겠습니다. 이것이 주제를 심층적으로 다룰 수 있는 유일한 방법이기 때문입니다.
JavaScript는 적용할 변환을 어떻게 결정합니까?
다양한 상황에서 발생하는 유형 변환에는 세 가지 변형이 있습니다. 사양에 설명된 대로 이를 "힌트"라고 합니다.
"string"
객체에서 문자열로의 변환의 경우, alert
와 같이 문자열이 필요한 객체에 대해 작업을 수행할 때:
// output alert(obj); // using object as a property key anotherObj[obj] = 123;
"number"
수학을 할 때와 같이 객체를 숫자로 변환하는 경우:
// explicit conversion let num = Number(obj); // maths (except binary plus) let n = +obj; // unary plus let delta = date1 - date2; // less/greater comparison let greater = user1 > user2;
대부분의 내장 수학 함수에는 이러한 변환도 포함됩니다.
"default"
연산자가 어떤 유형을 기대하는지 "확실하지 않은" 경우 드물게 발생합니다.
예를 들어, 이진 더하기 +
문자열(연결)과 숫자(더하기) 모두에서 작동할 수 있습니다. 따라서 바이너리 플러스가 객체를 인수로 가져오면 "default"
힌트를 사용하여 변환합니다.
또한 문자열, 숫자 또는 기호와 함께 ==
사용하여 객체를 비교하는 경우 어떤 변환을 수행해야 하는지도 불분명하므로 "default"
힌트가 사용됩니다.
// binary plus uses the "default" hint let total = obj1 + obj2; // obj == number uses the "default" hint if (user == 1) { ... };
<
>
와 같은 크고 작은 비교 연산자는 문자열과 숫자 모두에 사용할 수 있습니다. 그래도 "default"
가 아닌 "number"
힌트를 사용합니다. 그것은 역사적인 이유 때문입니다.
그러나 실제로는 상황이 좀 더 간단합니다.
한 가지 경우( Date
객체, 나중에 배우겠습니다)를 제외한 모든 내장 객체는 "number"
와 동일한 방식으로 "default"
변환을 구현합니다. 그리고 우리도 아마 똑같이 해야 할 것입니다.
그래도 3가지 힌트를 모두 아는 것이 중요합니다. 곧 그 이유를 알게 될 것입니다.
변환을 수행하기 위해 JavaScript는 세 가지 객체 메서드를 찾아 호출하려고 합니다.
obj[Symbol.toPrimitive](hint)
호출 – 기호 키 Symbol.toPrimitive
(시스템 기호)가 있는 메서드(해당 메서드가 있는 경우)"string"
인 경우obj.toString()
또는 obj.valueOf()
중 무엇이든 호출해 보세요."number"
또는 "default"
인 경우obj.valueOf()
또는 obj.toString()
중 무엇이든 호출해 보세요. 첫 번째 방법부터 시작해 보겠습니다. 다음과 같이 변환 방법의 이름을 지정하는 데 사용해야 하는 Symbol.toPrimitive
라는 내장 기호가 있습니다.
obj[Symbol.toPrimitive] = function(hint) { // here goes the code to convert this object to a primitive // it must return a primitive value // hint = one of "string", "number", "default" };
Symbol.toPrimitive
메서드가 존재하는 경우 모든 힌트에 사용되며 더 이상 메서드가 필요하지 않습니다.
예를 들어, 여기에서는 user
개체가 이를 구현합니다.
let user = { name: "John", money: 1000, [Symbol.toPrimitive](hint) { alert(`hint: ${hint}`); return hint == "string" ? `{name: "${this.name}"}` : this.money; } }; // conversions demo: alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500
코드에서 볼 수 있듯이 user
변환에 따라 자체 설명 문자열 또는 금액이 됩니다. 단일 메소드 user[Symbol.toPrimitive]
모든 변환 사례를 처리합니다.
Symbol.toPrimitive
없으면 JavaScript는 toString
및 valueOf
메서드를 찾으려고 시도합니다.
"string"
힌트의 경우: toString
메소드를 호출하고 해당 메소드가 존재하지 않거나 기본 값 대신 객체를 반환하는 경우 valueOf
호출합니다(따라서 toString
문자열 변환에 대한 우선순위를 갖습니다).valueOf
호출하고, 존재하지 않거나 기본 값 대신 객체를 반환하는 경우 toString
호출합니다(따라서 valueOf
수학에서 우선순위를 가집니다). toString
및 valueOf
메소드는 고대부터 유래되었습니다. 이는 기호(기호는 오래 전에는 존재하지 않음)가 아니라 "일반적인" 문자열 이름 메서드입니다. 변환을 구현하는 대체 "구식" 방법을 제공합니다.
이러한 메서드는 기본 값을 반환해야 합니다. toString
또는 valueOf
객체를 반환하면 무시됩니다(메서드가 없는 것과 같습니다).
기본적으로 일반 객체에는 다음과 같은 toString
및 valueOf
메소드가 있습니다.
toString
메소드는 "[object Object]"
문자열을 반환합니다.valueOf
메소드는 객체 자체를 반환합니다.데모는 다음과 같습니다.
let user = {name: "John"}; alert(user); // [object Object] alert(user.valueOf() === user); // true
따라서 alert
등에서처럼 객체를 문자열로 사용하려고 하면 기본적으로 [object Object]
표시됩니다.
여기서는 혼란을 피하기 위해 완전성을 위해서만 valueOf
언급합니다. 보시다시피 객체 자체를 반환하므로 무시됩니다. 이유는 묻지 마세요. 역사적인 이유 때문입니다. 그래서 우리는 그것이 존재하지 않는다고 가정할 수 있습니다.
변환을 사용자 정의하기 위해 이러한 메소드를 구현해 보겠습니다.
예를 들어 여기서 user
Symbol.toPrimitive
대신 toString
과 valueOf
의 조합을 사용하여 위와 동일한 작업을 수행합니다.
let user = { name: "John", money: 1000, // for hint="string" toString() { return `{name: "${this.name}"}`; }, // for hint="number" or "default" valueOf() { return this.money; } }; alert(user); // toString -> {name: "John"} alert(+user); // valueOf -> 1000 alert(user + 500); // valueOf -> 1500
보시다시피 동작은 이전 Symbol.toPrimitive
예제와 동일합니다.
종종 우리는 모든 기본 변환을 처리하기 위해 단일 "포괄" 장소를 원합니다. 이 경우 다음과 같이 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
메서드가 "number"
힌트에 대한 숫자를 반환하는지 여부는 제어할 수 없습니다.
유일한 필수 사항: 이 메서드는 객체가 아닌 기본 요소를 반환해야 합니다.
역사적인 이유로 toString
또는 valueOf
객체를 반환하면 오류는 없지만 해당 값은 무시됩니다(메서드가 존재하지 않는 것처럼). 그 이유는 고대에는 JavaScript에 좋은 "오류"라는 개념이 없었기 때문입니다.
대조적으로, Symbol.toPrimitive
더 엄격하므로 기본 요소를 반환 해야 하며 , 그렇지 않으면 오류가 발생합니다.
이미 알고 있듯이 많은 연산자와 함수는 유형 변환을 수행합니다. 예를 들어 곱셈 *
피연산자를 숫자로 변환합니다.
객체를 인수로 전달하면 두 가지 계산 단계가 있습니다.
예를 들어:
let obj = { // toString handles all conversions in the absence of other methods toString() { return "2"; } }; alert(obj * 2); // 4, object converted to primitive "2", then multiplication made it a number
obj * 2
먼저 객체를 기본 요소(문자열 "2"
)로 변환합니다."2" * 2
는 2 * 2
가 됩니다(문자열은 숫자로 변환됩니다).Binary plus는 문자열을 기꺼이 받아들이기 때문에 동일한 상황에서 문자열을 연결합니다.
let obj = { toString() { return "2"; } }; alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation
객체에서 프리미티브로의 변환은 프리미티브를 값으로 기대하는 많은 내장 함수와 연산자에 의해 자동으로 호출됩니다.
3가지 유형(힌트)이 있습니다.
"string"
(문자열이 필요한 alert
및 기타 작업용)"number"
(수학용)"default"
(몇몇 연산자, 일반적으로 객체는 "number"
와 같은 방식으로 구현함)사양에서는 어떤 연산자가 어떤 힌트를 사용하는지 명시적으로 설명합니다.
변환 알고리즘은 다음과 같습니다.
obj[Symbol.toPrimitive](hint)
호출하고,"string"
인 경우obj.toString()
또는 obj.valueOf()
중 무엇이든 호출해 보세요."number"
또는 "default"
인 경우obj.valueOf()
또는 obj.toString()
중 무엇이든 호출해 보세요.이러한 모든 메서드는 작동하려면 기본 요소를 반환해야 합니다(정의된 경우).
실제로는 로깅이나 디버깅 목적으로 객체의 "사람이 읽을 수 있는" 표현을 반환해야 하는 문자열 변환을 위한 "만능" 메서드로 obj.toString()
만 구현하는 것만으로도 충분한 경우가 많습니다.