[javascript]자바스크립트 객체를 원시형으로 변환하기

rondido·2022년 9월 6일
0

Javascript

목록 보기
20/21

객체를 원시형으로 변환하기

  • obj1 + obj2처럼 객체끼리 더하는 연산을 하거나, obj1-obj2처럼 객체끼리 빼는 연산을 하면 어떤 일이 일어날까요? console.log(obj)로 객체를 출력할 때는 무슨 일이 발생할까요?

  • 이 모든 경우에 자동 형 변환이 일어남. 객체는 원시값으로 변환되고, 그 후 의도한 연산이 수행

  • 형 변환 챕터에선 객체의 형 변환은 다루지 않음. 원시형 자료가 어떻게 문자, 숫자, 논리형으로 변환되는지만 알아보았죠. 이젠 메서드와 심볼에 대한 지식을 갖추었으니 본격적으로 이 공백을 메꿔 봅시다.

  1. 객체는 논리 평가 시 true를 반환 단 하나의 예외도 없죠. 따라서 객체는 숫자형이나 문자형으로만 형변환이 일어난다고 생각

  2. 숫자형으로의 형 변환은 객체끼리 빼는 연산을 할때나 수학 관련 함수를 적용할 때 일어남. 객체 date끼리 차감하면(date1-date2)두 날짜의 시간 차이가 반환 Date에 대해선 Date 객체와 날짜에서 다룰 예정

  3. 문자형으로의 형 변환은 대개 console.log(obj)같이 객체를 출력하려고 할 때 일어남

ToPrimitive

  • 특수 객체 메서드를 사용하면 숫자형이나 문자형으로의 형 변환을 원하는 대로 조절

  • 객체 형 변환은 세 종류로 구분되는데, 'hint'라 불리는 값이 구분 기준이 됨. 'hint'가 무엇인지는 명세서에 자세히 설명되어 있는데, '목표로 하는 자료형'정도로 이해하면 됨.

  • "String"

  • alerat 함수같이 문자열을 기대하는 연산을 수행할 때는 (객체-문자형 변환), 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가 됩니다. 아주 드물게 발생

  • 이항 덧셈 연산자 +는 피연산자의 자료형에 따라 문자열을 합치는 연산을 할 수도 있고 숫자를 더해주는 연산을 할 수도 있습니다. 따라서 +의 인수가 객체일은 hint가 default가 됩니다.

  • 동등 연산자 ==를 사용해 객체-문자형, 객체-심볼형끼리 비교할 때도 객체를 어떤 자료형으로 바꿔야 할지 확신이 안 서므로 hint는 default가 됨.

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

// obj == number 연산은 hint로 `default`를 사용합니다.
if (user == 1) { ... };
  • 크고 작음을 비교할 때 쓰이는 연산자 < ,> 역시 피연산자에 문자형과 숫자형 둘 다를 허용하는데, 이 연산자들은 hint를 'number'로 고정 hint가 'default'가 되는일이 없죠.

  • 실제 일을 할 때는 이런 사항을 모두 외울 필요가 없습니다. Date 객체를 제외한 모든 내장 객체는 hint가 "default"인 경우와 "number"인 경우를 동일하게 처리하기 때문 우리도 커스텀 객체를 만들 땐 이런 규칙을 따르면 됩니다.

  • "boolean" hint는 없습니다.

    • hint는 총 세가지입니다. 아주 간단하죠.
    • 'boolean' hint는 존재하지 않습니다. 모든 객체는 그냥 true로 평가. 게다가 우리도 내장 객체에 사용되는 규칙처럼 "default"와 "number"를 동일하게 처리하면, 결국엔 두 종류의 형 변환(객체-문자형,객체-숫자형)만 남게 됩니다.
  • 자바스크립트는 형 변환이 필요할 때, 아래와 같은 알고리즘에 따라 원하는 메서드를 찾고 호출
    1. 객체에 objSymbol.toPrimitvie메서드가 있는지 찾고, 있다면 메서드 호출
    2. 1에 해당하지 않고 hint가 "string"이라면,
    - obj.toString()이나 obj.valueOf()를 호출(존재하는 메서드만 실행)
    3. 1과 2에 해당하지 않고, hint가 "number"나 "default"라면
    - obj.valueOf()나 obj.toString()을 호출


Symbol.toprimitive

  • 첫 번째 메서드부터 살펴봅시다. 자바스크립트엔 Symbol.toPrimitive라는 내장 심볼이 존재 이 심볼은 아래와 같이 목표로 하는 자료형을 명명
obj[Symbol.toPrimitive] = function(hint){
	// 반드시 원시값을 반환
    // hint는 "string", "number", "default"중 하나가 될 수 있다.

}
  • 실제 돌아가는 예시를 살펴보는게 좋을 것 같다. user 객체 객체-원시형 변환 메서드 objSymbol.toPrimitvie를 구현
let user = {
  name: "John",
  money: 1000,

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

// 데모:
console.log(user); // hint: string -> {name: "John"}
console.log(+user); // hint: number -> 1000
console.log(user + 500); // hint: default -> 1500
  • 이렇게 메서드를 구현해 놓으면 user는 hint에 따라 (자기 자신을 설명해주는)문자열로 변환되기도 하고 (가지고 있는 돈의 액수를 나타내는)숫자로 변환되기도 합니다 user[Symbol.toPrimitive]를 사용하면 메서드 하나로 모든 종류의 형 변환을 다룰 수 있다.

toString과 valueOf

  • toString과 valueof는 심볼이 생기기 이전부터 존재해 왔던 '평범한'메서드 입니다. 이 메서드를 이용하면 '구식'이긴 하지만 형 변환을 직접 구현

  • 객체에 Symbol.toPrimitive가 없으면 자바스크립트는 아래 규칙에 따라 toString이나 valueOf를 호출

    • hint가 'string'인 경우: toString -> valueOf 순(toString이 있다면 toString을 호출, toString이 없다면 valueOf를 호출)
    • 그 외 : valueOf -> toString 순
  • 이 메서드들은 반드시 원시값을 반환. toString이나 valueOf가 객체를 반환하면 그 결과는 무시 마치 메서드가 처음부터 없었던 것처럼 되어버리죠.

  • 일반 객체는 기본적으로 toString과 valueOf에 적용되는 다음 규칙
    - toString은 문자열 "[object object]"을 반환
    - valueOf는 객체 자신을 반환

  • 데모를 살펴봅시다.

let user = {name:"John"};
console.log(user);
console.log(user.valueOf() === user); //true
  • 이런 이유 때문에 console.log에 객체를 넘기면 [object object]가 출력

  • 여기서 valueOf는 튜도리얼의 완성도를 높이고 헷갈리는 것을 줄여주려고 언급 앞서 본 바와 같이 valueOf는 객체 자신을 반환하기 때문에 그 결과가 무시됩니다. 왜 그런거냐고 묻지는 말아주세요. 역사적인 이유. 우리는 그냥 이 메서드가 존재하지 않는다고 생각

  • 이제 직접 이 메서드들ㅇ르 사용한 예시를 구현

  • 아래 user는 toString과 valueOf를 조합해 만들었는데, Symbol.toPrimitvie를 사용한 위쪽 예시

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

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

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

};

console.log(user); // toString -> {name: "John"}
console.log(+user); // valueOf -> 1000
console.log(user + 500); // valueOf -> 1500
  • 출력 결과가 Symbol.toPrimitive를 사용한 예제와 완전히 동일하는 걸 확인
  • 그런데 간혹 모든 형 변환을 한 곳에서 처리해야하는 경우 이럴 땐 아래와 같이 toString만 구현해주면 됩니다.
let user ={
	name: "John",
    toString(){
    	return 	this.name;
    }
};

console.log(user);
console.log(user + 500);
  • 객체에 Symbol.toPrimitive와 valueOf가 없으면, toString이 모든 형 변환 처리

반환 타입

  • 위에서 소개해드린 세 개의 메서드는 'hint'에 명시된 자료형으로의 형 변환을 보장해 주지 않음

  • toString()이 항상 문자열을 반환하리라는 보장이 없고, Symbol.toPrimitive의 hint가 "number"일 때 항상 숫자형 자료가 반환되리라는 보장이 없습니다.

  • 확신할 수 있는 단 한 가지는 객체가 아닌 원시값을 반환

  • 과거의 잔재
    - toString이나 valueOf가 객체를 반환해도 에러가 발생하지 않는다. 다만 이때는 반환 값이 무시되고, 메서드 자체가 존재하지 않았던 것처럼 동작합니다. 이렇게 동작하는 이유는 과거 자바스크립트엔 '에러'라는 개념이 잘 정립되어있지 않았기 때문

    • Symbol.toPrimitvie는 무조건 원시자료를 반환, 그렇지 않다면 에러가 발생

추가 형 변환

  • 지금까지 살펴본 바와 같이 상당수의 연산자와 함수가 피연산자의 형을 변환 곱셈을 해주는 연산자 *는 피연산자를 숫자형으로 변환

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

    • 객체는 원시형으로 변화
    • 변환후 원시 값이 원하는 형이 아닌 경우엔 또다시 형 변환이 발생
let obj = {
  // 다른 메서드가 없으면 toString에서 모든 형 변환을 처리합니다.
  toString() {
    return "2";
  }
};

console.log(obj * 2); // 4, 객체가 문자열 "2"로 바뀌고, 곱셈 연산 과정에서 문자열 "2"는 숫자 2로 변경됩니다.
  • obj * 2에선 객체가 원시형으로 변화되므로 toString에의해 obj는 문자열 "2"가 됩니다.

  • 곱셈 연산은 문자열은 숫자형으로 변환시키므로 "2" 2는 2 2가 됩니다.

  • 그런데 이항 덧셈 연산은 위와 같은 상황에서 문자열을 연결합니다.

let obj = {
  toString() {
    return "2";
  }
};

alert(obj + 2); // 22("2" + 2), 문자열이 반환되기 때문에 문자열끼리의 병합이 일어났습니다.

위 내용은 javascript.info 사이트에서 공부한 내용 정리
자세한 내용은 아래 링크를 참고하세요.
https://ko.javascript.info/object-toprimitive

profile
개발 옆차기

0개의 댓글