# 08. Object to Primitive

Бывают операции, при которых объект должен быть преобразован в примитив. Например:

* Преобразование в строковый тип — если объект выводится через `alert(obj)`.
* Преобразование в численный тип — при арифметических операциях, сравнении с примитивом.
* Преобразование в логический тип — при `if(obj)` и других логических операциях.

Основные правила:

* Любой объект в логическом контексте — `true`, даже если это пустой массив `[]` или объект `{}`.
* Само преобразование выполняется по алгоритму `ToPrimitive`.
* Если на объекте определен символ `@@toPrimitive`, то вызывается он.
* Для объекта в строковом контексте вызывается метод `toString`.
* Для численного преобразования объекта используется метод `valueOf`, а если его нет — то `toString`.

## Operation `ToPrimitive`

Converting an arbitrary value to a primitive is handled via the spec-internal operation `ToPrimitive(input, ?PreferredType)` takes an with input argument and an optional argument `PreferredType` which has three modes:

* `"number"`: the caller needs a number.
* `"string"`: the caller needs a string.
* `"default"`: the caller needs either a number or a string. The default mode is only used by:
  * Equality operator (`==`)
  * Addition operator (`+`)
  * `new Date(value)` (exactly one parameter!)

If the value is a primitive then `ToPrimitive()` is already done. Otherwise, the value is an object `obj`, which is converted to a primitive as follows:

* **Number mode**: Return the result of `obj.valueOf()` if it is primitive. Otherwise, return the result of `obj.toString()` if it is primitive. Otherwise, throw a `TypeError`.
* **String mode**: works like Number mode, but `toString()` is called first, `valueOf()` second.
* **Default mode**: works exactly like Number mode.

## `toPrimitive` operation in the spec

The abstract operation `ToPrimitive` converts its `input` argument to a *non-Object* type. If an object is capable of converting to more than one primitive type, it may use the optional hint `PreferredType` to favour that type. Conversion occurs according to the following algorithm:

1. If `Type(input)` is `Object`, then:
   1. If `PreferredType` was not passed, let `hint` be `"default"`.
   2. Else if `PreferredType` is hint `String`, let `hint` be `"string"`.
   3. Else `PreferredType` is hint `Number`, let `hint` be `"number"`.
   4. Let `exoticToPrim` be ?`GetMethod(input, @@toPrimitive)`.
   5. If `exoticToPrim` is not `undefined`, then
      1. Let `result` be `Call(exoticToPrim, input, hint)`.
      2. If `Type(result)` is not `Object`, return `result`.
      3. Throw a `TypeError` exception.
   6. If hint is `"default"`, set hint to `"number"`.
   7. Return `OrdinaryToPrimitive(input, hint)`.
2. Return `input`.

When the abstract operation `OrdinaryToPrimitive(O, hint)` is called with arguments `O` and `hint`, the following steps are taken:

1. Assert: `Type(O)` is `Object`.
2. Assert: `Type(hint)` is `String` and its value is either `"string"` or `"number"`.
3. If `hint` is `"string"`, then:
   1. Let `methodNames` be `["toString", "valueOf"]`.
4. Else,
   1. Let `methodNames` be `["valueOf", "toString"]`.
5. For each name in `methodNames` do
   1. Let method be `Get(O, name)`.
   2. If `IsCallable(method)` is `true`, then
      1. Let `result` be `Call(method, O)`.
      2. If `Type(result)` is not `Object`, return `result`.
6. Throw a `TypeError` exception.

## Symbol `@@toPrimitive`

`Symbol.toPrimitive` lets an object customize how it is coerced (converted automatically) to a primitive value. Значением этого символа должна быть функция, возвращающая значение одного из примитивных типов. Эта функция вызывается в качестве первой при преобразовании объекта в примитив по алгоритму `ToPrimitive` и имеет приоритет над другими функциями преобразования.

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

// An object with Symbol.toPrimitive property.
const 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"
```

## Operations `toString` and `valueOf`

Все объекты наследуют два метода преобразования:

* Метод `toString()` он возвращает строковое представление объекта. Если переменная является объектом, то по умолчанию метод `toString` выводит `[object <Tип>]`. Обратиться к этой нативной реализации `toString` можно `Object.prototype.toString.call(obj)`.
* Метод `valueOf()`. Задача этого метода определена нечетко: предполагается, что он должен преобразовать объект в представляющее его простое значение, если такое значение существует. Объекты по своей природе являются составными значениями, и большинство объектов не могут быть представлены в виде единственного простого значения, поэтому по умолчанию метод `valueOf()` возвращает не простое значение, а сам объект (`this`). Метод `valueOf` обязан возвращать примитивное значение, иначе его результат будет проигнорирован. При этом — не обязательно числовое.

```javascript
> const empty = {};
> empty.toString() // => '[object Object]'

> const empty = {};
> empty.valueOf() === empty // => true
```

Примеры использования `valueOf`:

* Классы-обертки определяют методы `valueOf()`, возвращающие обернутые простые значения.
* Массивы, функции и регулярные выражения наследуют метод по умолчанию. Вызов метода `valueOf()` экземпляров этих типов возвращает сам объект.
* Класс `Date` определяет метод `valueOf()`, возвращающий дату во внутреннем представлении: количество миллисекунд, прошедших с 1 января 1970 года.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://strctr.gitbook.io/programming/01-languages/javascript/01-language/b-structured/b2-objects/08-object-to-primitive-conversion.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
