valueOf 메소드, 뭐하러 있냐?
Tags: javascript, tutorial, korean, node
Published: 2021-10-25 08:20:14

언제부턴가 dev.to 사이트에 4개 제한했던 태그 개수 늘려줬네...

자, 개발을 하다보면 자동완성 하다보면,
일단 `toString()` 메소드는 많이 익숙할 것이다.
네가 만든 객체 및 클래스를 문자열로 표현하는 것. 그것은 익숙하니까. (끄덕)
그럼 `valueOf()` 메소드는 왜 있는가?
나답게 쿨거래하듯이 설명해 주겠다.
따라와라.

`Date` 객체 많이들 쓰고 있을 것이다. 이것이 편하든 불편하든 간에 많이들 쓸 것이다.
`Date` 객체를 다루다 보면 알게 되는게,

- `+new Date()` 가 먹힌다. 결과는 `new Date().getTime()` 과 동일하다.
- `Date` 객체 간 비교가 가능하다. `oldDate < newDate` 식이 잘 먹힌다.

이러하다. 이게 가능한 이유가 바로 `valueOf()` 이다.
자, `Date.prototype.getTime()` 메소드를 호출하면 나오는 유닉스 밀리초 숫자가 나온다. JS 에서 날짜 비교하거나 뭐 여러가지 다양한 용도에 활용할 수 있다.
그럼 `+new Date()` 가 왜 먹히냐면, 더하기 문자가 `valueOf()` 메소드의 축약어로 사용할 수 있기 때문이다.
그렇다면 `< >` 등의 비교문을 가능하게 한 것도? 맞다. `valueOf()` 메소드를 호출해서 비교하기 때문에 가능하다는 것이다.
좋다. `valueOf()` 메소드의 설명을 [MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf)에서 보도록 하자.

> valueOf() 메서드는 특정 객체의 원시 값을 반환합니다.

원시 값이라면 생각나는게 숫자일 것이다. 부울 값도 있고.
자바스크립트는 추가적으로 `[]` 배열 식과, `"문자열"` 문자열 식도 원시 값으로 취급한다. 심지어 `{}` 같은 객체 식도. 원시 객체라 한다.
따라서, 문자열의 경우 `"문자열".valueOf()` 호출하면 문자열 그대로 나올 것이다.
어쨌든, 원시 값이면 된다고 보면 된다. 물론 보통 숫자를 많이 쓸 것이다. 여러모로 유용하니까.

그렇다면, 자신의 객체나 클래스에 `valueOf()` 재정의 하고 싶다?
쉽다. `toString()` 재정의 기억나는가? 그거와 똑같이 하면 된다.

```js
MyObj.prototype.valueOf = function() {
  return this.numberValue; // 보통 숫자로 쓰겠지?
}
```

물론 문자열이어도 되고, 부울값이나 심지어 배열이어도 된다.
그렇다면 이녀석의 활용은?

가장 대표적인 활용처라 하면 '비교'가 되겠다.

자바는 `Comparable<V>`, 닷넷은 `IComparable<V>`(보다는 그냥 연산자 오버로딩 써도 됨)이나 파이썬의 연산자 데코레이터(`__le__` 등) 등등... 뭐 언어별로 다양한 비교 방법을 제공한다.
자바스크립트는 연산자 오버로딩도, 시스템 클래스가 제공하는 데코레이션도 없지만, `valueOf()` 메소드에 비교 가능한 원시 값(주로 숫자)을 뿌리면, 비교 연산을 통해 손쉽게 비교 가능하다. `Date` 객체처럼 말이다.

마지막으로, 아마 이런 의문을 들 것이다.
만약 문자열이 원시 값이라면, `toString()` 이 있는데, 이거 쓰는 것과 무슨 차이가 있냐고 의문이 들 것이다.
이건, ECMAScript 영문 공식 문서 [11.6.1 The Addition operator ( + )](https://262.ecma-international.org/5.1/#sec-11.6.1) 에서 답을 찾을 수 있는데,

{% stackoverflow 2486760 %}

```py
a + b:
    pa = ToPrimitive(a)
    pb = ToPrimitive(b)*
    if(pa is string || pb is string)
       return concat(ToString(pa), ToString(pb))
    else
       return add(ToNumber(pa), ToNumber(pb))
```

연산자가 들어가면, 원시 값을 찾으려 애를 쓰기 시작한다.
여기서 자바스크립트에서 원시 값을 정의하는 메소드라면 `valueOf` 가 있을 것이다. 바로 여기서 반환되는 값을 우선 짚어서 연산을 시도한다. 예를 들어 숫자끼리 더하고, 문자열 들어라면 문자열에 추가하는 등 말이다.
또한, 여기서 알 수 있는 사실은, 문자열이 우선 적용되어, 연산자와 피연산자 중 하나라도 문자열이 들어가는 순간 문자열 첨가를 시도하려 한다는 것을 알 수 있다. 그다음 숫자로 변환을 시도해서 연산을 시도한다.
만약 이도저도 아니라면? `TypeError` 오류가 널 반겨줄 것이다.

{% stackoverflow 2485794 %}

```js
var x = {
    toString: function () { return "foo"; },
    valueOf: function () { return 42; }
};

alert(x); // foo
"x=" + x; // "x=42"
x + "=x"; // "42=x"
x + "1"; // 421
x + 1; // 43
["x=", x].join(""); // "x=foo"
```

만약 `toString()` 메소드와 `valueOf()` 메소드를 모두 재정의 했다면, 연산자 및 피연산자의 우선순위는 `valueOf()` 메소드 결과 값이 되는 것이다.
그러면, `alert(myobj)` 및 `['1',2,myObj].join(',')` 식을 수행했을 때는 `toString()` 으로 정의된 문자열로 나오는데?
그건 바로 그 함수에서 문자열을 가져와야 하기 때문에 `toString()` 메소드를 쓴 것이다.
그러니까 이렇게 정리하면 된다.

- `valueOf()` 연산을 위한 값. 원시 값을 뱉어서 연산에 필요한 기능 제공!
- `toString()` 문자열로 표시할 값. 오로지 문자열로만!

만약 문자열만 표시할 목적이라면, `toString()` 메소드를 쓰면 된다. 이 메소드만 써도 `+` 등의 문자열 첨가 연산에 대응 가능하다.
만약 비교 등의 객체의 원시 기능 역할을 수행하고자 한다면, `valueOf()` 메소드를 쓰면 된다. 숫자를 제공해서 비교에 쓰이거나, 문자열을 뱉어내도 되고, 배열이나 원시 객체 뱉어내도 되고... 원시 값을 통한 적절한 연산에 필요한 값을 뿌리면 되는 거다.

자, 이제 정리가 됐는가? 그럼 됐다. 난 그냥 보람을 느끼겠다.
끗.

**2021-10-26**
아 한가지 깜박한게 있는데, 너희들도 알다시피 문자열도 비교 연산이 가능하다. 그렇다면 굳이 재정의 안해도? 대응 가능하다. 만약 너의 쌩 객체에 `valueOf()` 메소드 호출하면 `[object yourType]` 같은 형식을 가진 문자열이 표출되니까. 그래서 재정의를 안해도 `< >` 같은 비교식이 먹힌다. 물론 그 결과가 옳은지는 며느리도 모른다. (대충 틀니 소리)

`끗_최종.hwp`