← Назад к вопросам

Зачем нужен Simbol.toPrimitive?

2.0 Middle🔥 131 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зачем нужен Symbol.toPrimitive?

Symbol.toPrimitive — это встроенный символ JavaScript, который позволяет объекту определить, как он должен быть преобразован в примитивное значение. Это критически важно для контроля поведения объекта при неявном преобразовании типов в различных контекстах.

Когда происходит преобразование в примитив?

JavaScript часто должен преобразовать объект в примитивное значение (строку или число) неявно:

const obj = { value: 42 };

// Контексты, требующие примитива
const str = obj + "" ;          // Конкатенация со строкой
const num = obj + 1;            // Арифметика
const comparison = obj > 5;      // Сравнение
const template = `${obj}`;       // Шаблонные строки
if (obj) { }                     // Булевой контекст (не использует toPrimitive)

Без явного определения поведения JavaScript будет использовать стандартное преобразование, которое часто не является желаемым.

Как работает Symbol.toPrimitive?

const customObject = {
  value: 100,
  [Symbol.toPrimitive](hint) {
    console.log("Подсказка преобразования:", hint);
    
    if (hint === "number") {
      return this.value;
    }
    if (hint === "string") {
      return `Custom: ${this.value}`;
    }
    if (hint === "default") {
      return this.value;
    }
  }
};

console.log(customObject + 0);        // Вывод: number, результат: 100
console.log(String(customObject));    // Вывод: string, результат: "Custom: 100"
console.log(customObject == 100);     // Вывод: default, результат: true
console.log(`${customObject}`);       // Вывод: default, результат: 100

Три типа подсказок (hint):

  1. "number" — когда объект используется в арифметических операциях или сравнениях
  2. "string" — когда требуется строковое представление (String(), шаблоны)
  3. "default" — в двусмысленных ситуациях (сравнение с ==, арифметика со строками)

Практические примеры

1. Класс Валюты

class Currency {
  constructor(amount, code) {
    this.amount = amount;
    this.code = code;
  }
  
  [Symbol.toPrimitive](hint) {
    if (hint === "number") {
      return this.amount;
    }
    if (hint === "string") {
      return `${this.amount} ${this.code}`;
    }
    return this.amount;
  }
}

const price = new Currency(29.99, "USD");
console.log(price + 10);          // 39.99 (число)
console.log(`Цена: ${price}`);    // "Цена: 29.99 USD" (строка)
console.log(price > 20);          // true (число)

2. Класс для логирования

class Logger {
  constructor(name, level) {
    this.name = name;
    this.level = level;
  }
  
  [Symbol.toPrimitive](hint) {
    if (hint === "string") {
      return `[${this.level.toUpperCase()}] ${this.name}`;
    }
    // Для number hint возвращаем приоритет
    return this.level === "error" ? 1 : 0;
  }
}

const log = new Logger("app", "error");
console.log(String(log));  // "[ERROR] app"
console.log(log + 0);      // 1

3. Класс Даты с кастомным форматом

class SmartDate {
  constructor(date) {
    this.date = new Date(date);
  }
  
  [Symbol.toPrimitive](hint) {
    if (hint === "string") {
      return this.date.toLocaleDateString("ru-RU");
    }
    // Для number возвращаем timestamp
    return this.date.getTime();
  }
}

const today = new SmartDate("2026-04-02");
console.log(`Сегодня: ${today}`);  // "Сегодня: 02.04.2026"
console.log(today - new Date());    // Разница в миллисекундах

Сравнение с другими методами преобразования

const obj = {
  value: 42,
  
  toString() {
    return "toString result";
  },
  
  valueOf() {
    return this.value;
  },
  
  [Symbol.toPrimitive](hint) {
    return `toPrimitive: ${hint}`;
  }
};

// Symbol.toPrimitive имеет приоритет
console.log(String(obj));   // "toPrimitive: string"
console.log(Number(obj));   // NaN (из "toPrimitive: number")
console.log(obj + "");      // "toPrimitive: default"

Порядок приоритета:

  1. Symbol.toPrimitive (если определен)
  2. valueOf() (для hint === "number")
  3. toString() (для hint === "string")

Заключение

Symbol.toPrimitive — это мощный инструмент для создания объектов, которые ведут себя как примитивы в различных контекстах. Это особенно полезно для:

  • Создания классов-обёрток (Currency, Date, Logger)
  • Обеспечения интуитивного поведения при преобразованиях
  • Контроля приоритетов преобразования типов
  • Повышения читаемости кода при работе с объектами