본문 바로가기

모던 자바스크립트

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

1. 객체는 논리 평가 시 true를 반환한다. 따라서 객체는 숫자형이나 문자형으로만 형 변환이 일어난다.

2. 숫자형으로의 형 변환은 객체끼리 빼는 연산을 할 때나 수학 관련 함수를 적용할 때 일어난다.

     (객체 Date끼리 차감하면 두 날짜의 시간 차이가 반환된다.)

3. 문자형으로의 형 변환은 대게 alert(obj)같이 객체를 출력하려고 할 때 일어난다. 

 

ToPrimitive

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

◾️ 객체 형 변환은 'hint'라 불리는 값을 기준으로 세 종류로 구분된다. 

⚠️ hint : 목표로 하는 자료형

◾️ hint가 string일 때 : 문자열을 기대하는 연산을 수행할 때(객체 - 문자형 변환) 

// 객체를 출력
alert(obj);

// 객체를 프로퍼티 키로 사용
anotherObj[obj] = 123;

◾️ hint가 number일 때 : 수학 연산을 적용하려 할 때(객체 - 숫자형 변환) 

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

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

// 대소 비교
let greater = user1 > user2;

◾️ hint가 default일 때 : 연산자가 기대하는 자료형이 확실 치 않을때. (아주 드물게 발생)

◾️ 이항 덧셈 연산자 는 피연산자의 자료형에 따라 문자열을 합치는 연산일 수도 있고 숫자를 더해주는 연산을 할 수도 있다. 

    → 의 인수가 객체일 때는 hint가 default가 된다. 

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

// hint로 'default'를 사용

let total = obj1 + obj2; // 이항 덧셈 연산 

if(user == 1) { ... }; // obj == number 연산

 

◾️ 비교 연산자 <, > 도 피연산자에 문자형과 숫자형 둘 다를 허용하는데, 이 연산자들은 hint를 'number'로 고정한다.

 

⚠️ 'boolean' hint는 없다. 

◾️ 모든 객체는 true로 평가된다. 
◾️ 내장 객체에 사용되는 규칙처럼 'default'와 'number'를 동일하게 처리하면, 결국엔 두 종류의 형 변환 (객체 - 문자형, 객체 - 숫자형)만 남게 된다. 

자바스크립트는 형 변환이 필요할 때, 다음과 같은 알고리즘에 따라 원하는 메서드를 찾고 호출한다. 

1. 객체에 obj[Symbol.toPrimitive](hint) 메서드가 있는지 찾고, 있다면 메서드를 호출한다. 

     Symbol.toPrimitive는 시스템 심볼로, 심볼형 키로 사용된다. 

2. 1에 해당하지 않고 hint가 string이라면,

    ◾️ obj.toString()obj.valueOf()를 호출한다(존재하는 메서드만 실행됨)

3. 1과 2에 해당하지 않고, hint가 number나 default라면

    ◾️ obj.valueOf()obj.toString()을 호출한다(존재하는 메서드만 실행됨)

ℹ️ valueOf()

◾️ 특정 객체의 원시 값을 반환한다.
◾️ 보통 원시 값을 필요로 할 때 알아서 사용하므로 직접 호출해야 하는 경우는 매우 드물다. 

 

 

Symbol.toPrimitive

📢Symbol.toPrimitive : 내장 심볼, 목표로 하는 자료형(hint)를 명명하는데 사용된다. 

obj[Symbol.toPrimitive] = function(hint){
	// 반드시 원시값을 반환해야 한다. 
    // hint는 string, number, default중 하나
}

◾️ 메서드로 구현해 놓으면 user는 hint에 따라 문자열로 변환되기도 하고 숫자로 변환되기도 한다. 

◾️ user[Symbol.toPrimitive]를 사용하면 메서드 하나로 모든 종류의 형 변환을 다룰 수 있다. 

let user = {
  name: 'suzu',
  money: 10000,
  
  [Symbol.toPrimitive](hint){
  	console.log(`hint: ${hint}`);
    return hint == 'string' ? `{name: '${this.name}'}` : this.money;
  }
}

alert(user); // hint: string -> { name: 'suzu' }
alert(+user); // hint: number -> 10000
alert(user + 500); // hint: default -> 10500

ℹ️ MDN 예시

// An object without Symbol.toPrimitive property.
var obj1 = {};
console.log(+obj1);     // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ''); // "[object Object]"

// An object with Symbol.toPrimitive property.
var obj2 = {
  [Symbol.toPrimitive](hint) {
    if (hint == 'number') {
      return 10;
    }
    if (hint == 'string') {
      return 'hello';
    }
    return true;
  }
};
console.log(+obj2);     // 10        -- hint is "number"
console.log(`${obj2}`); // "hello"   -- hint is "string"
console.log(obj2 + ''); // "true"    -- hint is "default"

 

 

toString과 valueOf

◾️ 형 변환 메서드

◾️ 객체에 Symbol.toPrimitive가 없으면 자바스크립트는 다음 규칙에 따라 toString이나 valueOf를 호출한다. 

    ◾️ hint가 string인 경우 : toString  → valueOf순 (toString이 있다면 toString을 호출, 없다면 valueOf를 호출)

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

    ◾️ toString은 문자열 [object object]를 반환한다.  

    ◾️ valueOf는 객체 자신을 반환한다. 

let user = { name: 'suzu' };

alert(user); // [object Object]
alert(user.valueOf() === user); // true

◾️ Symbol.toPrimitive를 사용한 예시를 toString, valueOf를 사용하여 동일하게 동작하도록 구현

let user = {
  name: 'suzu',
  money: 10000,
  
  //hint가 'string'인 경우
  toString() {
  	return `{ name: '${this.name}'}`;
  },
  
  // hint가 'number'나 'default'인 경우
  valueOf() {
  	return this.money;
  }
};

alert(user); // {name: 'suzu'}
alert(+user); // 10000
alert(user + 500) // 10500

◾️ 모든 형 변환을 한 곳에서 처리해야 하는 경우 toString만 구현해준다. 

let user = {
  name: 'suzu',

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

alert(user); // suzu
alert(user + 500); // suzu500

 

반환 타입

◾️ Symbol.toPrimitive, toString(), valueOf()메서드는 'hint'에 명시된 자료형으로의 형 변환을 보장해 주지 않는다. 

◾️ 객체가 아닌 원시값을 반환해준다는 것만 확신할 수 있다. 

⚠️ toString이나 valueOf가 객체를 반환해도 에러가 발생하지 않고 반환 값이 무시되고 메서드 자체가 존재하지 않았던 것처럼 동작한다. → 과거 자바스크립트엔 '에러'라는 개념이 잘 정립되어있지 않았기 때문

반면에 Symbol.toPrimitive는 무조건 원시자료를 반환해야 한다. 그렇지 않으면 에러가 발생한다. 

 

 

추가 형 변환

1. 객체는 원시형으로 변화된다. 

2. 변환 후 원시값이 원하는 형이 아닌 경우엔 또다시 형 변환이 일어난다. 

◾️ obj * 2 에선 객체가 원시형으로 변화되므로 toString에 의해 obj는 문자열 '2'가 된다. 

◾️ 곱셈 연산은 문자열은 숫자형으로 변환시키므로 '2' * 2는 2*2가 된다. 

◾️ 이항 덧셈 연산은 문자열을 연결하기 때문에 '22'가 된다. 

let obj = {
  // 다른 메서드가 없으면 toString에서 모든 형 변환을 처리한다. 
  toString() {
    return '2';
  }
};

alert(obj * 2); // 4
alert(obj + 2); // 22

 

 

📝 요약

◽️ 원시값을 기대하는 내장 함수나 연산자를 사용할 때 객체-원시형으로의 형 변환이 자동으로 일어난다. 
◽️
객체-원시형으로의 형 변환은 hint를 기준으로 세 종류로 구분할 수 있다. 
     1. 'string' (alert같이 문자열을 필요로 하는 연산)
     2. 'number' (수학 연산)
     3. 'default' (드물게 발생 → 보통 '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()만 구현해도 충분한 경우가 많다.
    로깅이나 디버깅 목적으로 자주 사용된다. 

 

 

 

 



📝 오늘 새롭게 배운 내용 📝

 

✅ 객체도 원시값으로 나타낼 수 있다!!!

✅ 어떤 원시형으로 변할 건지 hint로 구분할 수 있다. 

✅ 이런 형 변환 때문에 타입스크립트가 나온건가.. ? 

✅ 조금 낯설긴 하지만 형변환을 조금은 이해할 수 있었다. 

 

 

 

📌 [JAVASCRIPT_INFO 객체를 원시형으로 변환하기]

 

 

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

 

ko.javascript.info

 

📌 [MDN Symbol.toPrimitive]

 

Symbol.toPrimitive

The Symbol.toPrimitive is a symbol that specifies a function valued property that is called to convert an object to a corresponding primitive value.

developer.mozilla.org

 

 

 

 

 

 

'모던 자바스크립트' 카테고리의 다른 글

6.2 나머지 매개변수와 전개 문법  (0) 2020.08.19
6.1 재귀와 스택  (0) 2020.08.15
4.7 심볼형  (0) 2020.08.12
4.6 옵셔널 체이닝 '?.'  (0) 2020.08.11
4.5 'new' 연산자와 생성자 함수  (0) 2020.08.10