Коли об'єкт опиняється там, де потрібен примітив (+obj, obj + '', `${obj}`, obj > 5, obj == 1), рушій викликає перетворення на примітив з «підказкою» (hint), якого типу очікують:
'number'- арифметика й порівняння:+obj,obj * 2,obj > 5;'string'- шаблонні рядки,String(obj), ключі об'єкта;'default'- коли незрозуміло: бінарний+,==.
Алгоритм:
- якщо є метод
obj[Symbol.toPrimitive](hint)- викликається він; - інакше для
'string'пробуютьсяtoString(), потімvalueOf(); - для
'number'і'default'- спершуvalueOf(), потімtoString(); - перший результат-примітив і використовується. Якщо обидва повернули об'єкт -
TypeError.
const price = {
amount: 42,
valueOf() { return this.amount; },
toString() { return `${this.amount} грн`; },
};
price + 1; // 43 (default → valueOf)
price * 2; // 84 (number → valueOf)
`${price}`; // '42 грн' (string → toString)
String(price); // '42 грн'
Повний контроль - Symbol.toPrimitive:
const money = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') return 42;
if (hint === 'string') return '42 грн';
return 'default';
},
};
+money; // 42
`${money}`; // '42 грн'
money + ''; // 'default'
Як це пояснює «дивацтва» JavaScript:
[] + []→'': масиви черезtoString()стають порожніми рядками;[] + {}→'[object Object]';[5] * 2→10:[5].toString()='5', потім число;Date- єдиний вбудований об'єкт, для якого'default'означає рядок:date + 1дає рядок з датою й одиницею, аdate - 1- число (мітку часу мінус 1).
Практична цінність:
- порівняння дат
a < bпрацює черезvalueOf(), що повертає мілісекунди; - власні числові типи (гроші, вектори) можуть поводитися природно в шаблонах, але арифметику через
valueOfкраще не будувати:price1 + price2дасть число й «загубить» валюту. Явні методи (add,format) надійніші; - в об'єктах-значеннях, що потрапляють у шаблони, корисний змістовний
toString()- замість[object Object]у логах і повідомленнях.