Как модифицировать класс в JavaScript для кастомного сравнения экземпляра с числом
Что проверяют
Знаете ли вы, как JavaScript приводит объект к примитиву и какие операторы это делают. Следом почти всегда спрашивают, почему не сработает === и что будет при сравнении двух экземпляров между собой.
Как отвечать
Перегрузки операторов в JavaScript нет, но при сравнении объекта с числом движок вызывает внутреннюю операцию ToPrimitive. Её и настраивают:
valueOf(): вызывается первым в числовом контексте. Если вернёт примитив, дальше пойдёт он, если объект, движок попробуетtoString();Symbol.toPrimitive: современный и более точный способ. Метод получает подсказкуhintсо значениемnumber,stringилиdefaultи сам решает, что вернуть. Если вернёт объект, будетTypeError;toString(): строковый контекст, например шаблонные строки иString(obj).
class Money {
constructor(amount) { this.amount = amount; }
[Symbol.toPrimitive](hint) {
return hint === 'string' ? `${this.amount} ₽` : this.amount;
}
}
new Money(100) > 50; // true
new Money(100) == 100; // trueРазберите, какой оператор что делает. <, >, <=, >= приводят оба операнда с подсказкой number. == с примитивом и бинарный + передают default. === не приводит никогда, поэтому new Money(100) === 100 всегда false.
Закончите оценкой: приём годится для объектов-значений вроде денег или дат, но неявное приведение ухудшает читаемость. TypeScript сравнение объекта с числом через < не пропустит, поэтому в рабочем коде чаще пишут явный метод compareTo() или equals().
Где спотыкаются
Ожидают, что == сравнит два экземпляра по значению. Когда оба операнда объекты, приведения нет, сравниваются ссылки, и new Money(1) == new Money(1) даёт false. При этом < и > между двумя объектами приведение делают.
Путают подсказки: переопределяют только toString() и удивляются, что сложение с числом склеивает строки, или ждут, что шаблонная строка вызовет valueOf().
Для подсказки default возвращают строку, и тогда money + 1 даёт строку вместо числа. Или возвращают из Symbol.toPrimitive объект и получают TypeError в самом неожиданном месте.