Ошибка NaN в консоли при вычислениях через Math чаще всего означает, что в переменную, объявленную через var, попала строка вместо числа — например, значение из поля ввода без преобразования. Проверьте тип данных через typeof до вызова математической функции: если результат "string", сначала примените Number() или parseFloat(). Это самая частая причина «сломанной математики» в скриптах начинающих разработчиков.
В этой статье разберём, как объявлять переменные через var, какие методы предоставляет встроенный объект Math в JavaScript, где возникают типичные ошибки и как писать вычисления, которые не ломаются на граничных значениях. Материал ориентирован на тех, кто только начинает работать с языком или хочет систематизировать базовые знания.
Объявление переменных через var: что нужно знать
Ключевое слово var — исторически первый способ объявления переменных в JavaScript. Переменная создаётся командой var x = 10;, после чего её значение можно менять сколько угодно раз. В отличие от let и const, у var функциональная область видимости: переменная «видна» во всей функции, а не только внутри блока фигурных скобок.
Особенность, о которой часто забывают, — всплытие (hoisting). Объявление через var интерпретатор поднимает в начало функции, поэтому обращение к переменной до строки объявления не вызовет ошибку, а вернёт undefined. С let в том же месте была бы ошибка ReferenceError. Это различие важно при отладке старого кода.
В современных проектах предпочтительны let и const, но var до сих пор встречается в легаси-коде, учебных примерах и старых библиотеках. Понимать его поведение необходимо, даже если в своём коде вы его не используете.
var имеет функциональную область видимости и всплывает — в новом коде используйте let и const, но читать var-код нужно уметь.
Объект Math: основные методы
Встроенный объект Math предоставляет набор статических методов и констант для вычислений. Его не нужно создавать через new — все методы вызываются напрямую: Math.round(4.6) вернёт 5. Ниже — наиболее востребованные функции.
- 🔢
Math.round(x)— округление до ближайшего целого; - ⬇️
Math.floor(x)— округление вниз,Math.ceil(x)— вверх; - ✂️
Math.trunc(x)— отбрасывание дробной части; - 📈
Math.max(a, b, ...)иMath.min(a, b, ...)— поиск крайних значений; - 🎲
Math.random()— случайное число от 0 (включительно) до 1 (не включительно); - 💪
Math.pow(x, n)или оператор**— возведение в степень.
Константы тоже доступны из коробки: Math.PI для числа π, Math.E для основания натурального логарифма. Они полезны в геометрических расчётах — например, площадь круга вычисляется как Math.PI r r.
Практические примеры вычислений
Рассмотрим типовую задачу — генерацию случайного целого числа в заданном диапазоне. Комбинация Math.random() с Math.floor() — стандартный приём:
var min = 1;
var max = 10;
var randomInt = Math.floor(Math.random() * (max - min + 1)) + min;
Здесь Math.random() даёт дробь от 0 до 1, умножение растягивает её на нужный диапазон, а Math.floor() отбрасывает дробную часть. Сдвиг на min переносит результат в целевой интервал.
Второй частый сценарий — округление денежных значений до двух знаков. Прямой вызов Math.round(price) не подойдёт: он округляет до целого. Используйте трюк с умножением и делением на 100:
var price = 19.987;
var rounded = Math.round(price * 100) / 100; // 19.99
Для денежных расчётов храните суммы в копейках (целых числах), а делите на 100 только при выводе — это исключает ошибки плавающей точки вида 0.1 + 0.2 !== 0.3.
Типичные ошибки и их диагностика
Главный источник сбоев — неявное приведение типов. Строка "10" в умножении сработает как число, но в сложении даст конкатенацию: "10" + 5 вернёт строку "105", а не 15. Перед математическими операциями явно преобразуйте входные данные.
Вторая группа проблем — погрешности плавающей точки. Выражение 0.1 + 0.2 в JavaScript даёт 0.30000000000000004, это особенность двоичного представления дробей, а не баг языка. Сравнивайте дробные числа с допуском, а не строгим равенством.
| Симптом | Вероятная причина | Что проверить |
|---|---|---|
| Результат NaN | Строка или undefined в вычислении | typeof всех операндов |
| "10" + 5 = "105" | Конкатенация вместо сложения | Явное преобразование Number() |
| 0.1 + 0.2 ≠ 0.3 | Погрешность float64 | Сравнение с допуском epsilon |
| undefined до объявления | Всплытие var | Порядок строк в функции |
| Math.max() вернул -Infinity | Вызов без аргументов | Проверка длины массива |
⚠️ Внимание: методыMath.max()иMath.min()не принимают массив напрямую. ВызовMath.max([1, 2, 3])вернётNaN. Используйте оператор расширения:Math.max(...arr), предварительно убедившись, что массив не пуст.
Пошаговая проверка вычислений в скрипте
Если формула в коде даёт неожиданный результат, действуйте по цепочке: сначала зафиксируйте входные данные, затем локализуйте шаг, на котором значение «ломается». Ниже — рабочий чек-лист.
☑️ Отладка математических вычислений
Разбивка длинного выражения на промежуточные переменные — приём, который экономит часы отладки. Вместо var result = Math.round((a + b) * c / d); напишите три строки с отдельными переменными и выведите каждую в консоль. Точка сбоя станет видна сразу.
Почему var считается устаревшим
var не имеет блочной области видимости: переменная, объявленная внутри if или цикла, видна во всей функции. Это провоцирует случайные переопределения. let и const, добавленные в стандарте ES2015, ограничены блоком и делают код предсказуемее. const дополнительно запрещает переприсваивание, что помогает отслеживать, какие значения меняются, а какие нет.
Var, let и const: что выбрать в новом коде
Для новых проектов действует простое правило: по умолчанию используйте const, а let — только когда значение действительно должно меняться. var оставьте для поддержки старого кода. Такой подход сужает область видимости до блока и делает намерения автора читаемыми.
Что делать, если вы правите легаси-файл, где всё объявлено через var? Менять объявления «вслепую» по всему файлу не стоит: из-за разницы в области видимости и всплытии поведение кода может измениться. Заменяйте точечно, проверяя, что переменная не используется до строки объявления и не переобъявляется в той же функции.
⚠️ Внимание: механическая заменаvarнаletв циклах с асинхронными колбэками меняет результат. Сvarвсе колбэки делят одну переменную, сletкаждая итерация получает свою копию. Иногда это исправляет баг, а иногда ломает логику, рассчитанную на старое поведение.
Включите в редакторе линтер (например, ESLint с правилом no-var) — он автоматически подсветит все устаревшие объявления и предложит безопасную замену.
Часто задаваемые вопросы
Чем var отличается от let?
У var функциональная область видимости и всплытие с инициализацией undefined. У let — блочная область видимости, а обращение до объявления вызывает ошибку. Повторное объявление одной переменной через var допустимо, через let — нет.
Как округлить число до двух знаков после запятой?
Умножьте на 100, примените Math.round(), затем разделите на 100. Для вывода строки с фиксированным количеством знаков есть метод toFixed(2), но он возвращает строку, а не число.
Почему Math.random() не подходит для паролей?
Math.random() не является криптографически стойким генератором — его последовательность предсказуема. Для токенов и паролей используйте crypto.getRandomValues(), доступный в браузерах и Node.js.
Что вернёт Math.max() без аргументов?
Значение -Infinity. Симметрично Math.min() без аргументов вернёт Infinity. Если вы передаёте данные из массива, который может оказаться пустым, проверяйте его длину заранее.
Можно ли создать экземпляр Math через new?
Нет. Math — не конструктор, а обычный объект с набором статических методов и констант. Все вычисления выполняются прямыми вызовами вида Math.sqrt(25).