Деньги любят порядок: как превратить число в красивую цену на JavaScript?
Представьте: вы пишете интернет-магазин. Сервер отдаёт цену товара — просто число 1999.5. Показывать пользователю «сырое» число — моветон. Клиент хочет видеть "$1,999.50" или, если вы в России, "1 999,50 ₽".
Этот вопрос мучает тысячи разработчиков на Stack Overflow. Сегодня мы раз и навсегда разберёмся, как правильно форматировать числа в валютные строки в JavaScript. Вы узнаете про встроенные методы, их особенности и когда какой способ лучше использовать.
Почему просто .toFixed(2) — это плохая идея?
Многие новички делают так:
const price = 1999.5;
console.log('$' + price.toFixed(2)); // "$1999.50"
Вроде бы работает. Но это — бомба замедленного действия. Почему?
- Нет разделителей разрядов. $1999.50 читается хуже, чем $1,999.50.
- Нет локализации. В Европе запятая — десятичный разделитель, точка — разделитель тысяч. Ваш код сломается.
- Жёстко зашит символ валюты. Поменять $ на € — значит лезть в код.
К счастью, современный JavaScript предоставляет нам элегантные инструменты. Давайте изучим их.
Способ №1: Intl.NumberFormat — король форматирования (рекомендуется)
Этот объект — часть Internationalization API. Он создан специально для работы с локалью, валютами и числами. Это самый правильный и гибкий способ.
Базовый пример
const formatter = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB'
});
console.log(formatter.format(1999.5)); // "1 999,50 ₽"
Всё! JavaScript сам расставил пробелы, запятые и добавил символ рубля. Магия, да?
Как это работает?
Конструктор принимает два аргумента:
- locales — строка с кодом локали (например,
'en-US','de-DE','ru-RU'). - options — объект настройки. Ключевое:
style: 'currency'иcurrency: 'USD'.
Настройка под любую валюту
Хотите доллары для США?
const usFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
});
console.log(usFormatter.format(2500)); // "$2,500.00"
Евро для Германии?
const deFormatter = new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR'
});
console.log(deFormatter.format(2500)); // "2.500,00 €"
Обратите внимание: в Германии точка — разделитель тысяч, а запятая — десятичный знак. Intl всё делает за вас.
Дополнительные опции
Хотите убрать дробную часть (центов нет)?
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
});
console.log(formatter.format(1999.5)); // "$2,000" (округлило)
Нужно показывать всегда 4 знака после запятой? Пожалуйста:
const preciseFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 4
});
console.log(preciseFormatter.format(1.2)); // "$1.2000"
Плюсы: нативный, быстрый, поддерживает все валюты мира, легко меняет локаль.
Минусы: не работает в очень старых браузерах (IE 10 и ниже — но это уже история).
Способ №2: Number.prototype.toLocaleString() — быстрый и простой
Этот метод — "младший брат" Intl.NumberFormat. Он встроен прямо в прототип числа и делает то же самое, но с меньшим контролем.
Пример
const price = 2500.75;
console.log(price.toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
})); // "$2,500.75"
Выглядит почти как Intl.NumberFormat, правда? Разница в том, что toLocaleString каждый раз создаёт новый объект форматтера "под капотом". Если вы форматируете много чисел в цикле, лучше использовать Intl.NumberFormat один раз и переиспользовать его метод .format().
Когда использовать toLocaleString?
- Для разового форматирования.
- В простых скриптах, где производительность не критична.
- Когда лень писать лишнюю строку с
new.
Плюсы: короткая запись, не нужно создавать отдельный объект.
Минусы: при массовом форматировании работает медленнее, чем Intl.NumberFormat.
Способ №3: Ручное форматирование — когда API не подходит
Бывают ситуации, когда встроенные методы не годятся. Например:
- Вы форматируете криптовалюты (BTC, ETH) — у них нет стандартных кодов в Intl.
- Вам нужен нестандартный формат, например, "USD 1 234.56" без знака валюты.
- Вы поддерживаете очень старые браузеры (IE 9 и ниже).
Тогда пишем свою функцию. Вот элегантный вариант:
function formatCurrency(amount, currencySymbol = '$', decimalSeparator = '.', thousandsSeparator = ',') {
// 1. Фиксируем 2 знака после запятой
let parts = amount.toFixed(2).split('.');
// 2. Добавляем разделители тысяч
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSeparator);
// 3. Собираем строку
return currencySymbol + parts.join(decimalSeparator);
}
console.log(formatCurrency(1234567.89)); // "$1,234,567.89"
console.log(formatCurrency(1234567.89, '€', ',', ' ')); // "€1 234 567,89"
Разберём регулярку: \B(?=(\d{3})+(?!\d)) — это "магический" паттерн, который находит позиции между разрядами тысяч. Он