> For the complete documentation index, see [llms.txt](https://strctr.gitbook.io/programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://strctr.gitbook.io/programming/01-languages/javascript/01-language/b-structured/b2-objects/08-object-to-primitive-conversion.md).

# 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 года.
