IsNumeric в JavaScript: 5 фатальных ошибок при валидации чисел

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Валидация десятичных чисел в JavaScript: почему IsNumeric() ломает код и как это исправить

Каждый разработчик, который писал на JavaScript хотя бы неделю, рано или поздно сталкивался с задачей: «Проверить, является ли строка числом». Казалось бы, что может быть проще? Встроенная функция isNaN() или parseFloat() — и дело в шляпе. Но не тут-то было. Вопрос «Validate decimal numbers in JavaScript — IsNumeric()» на Stack Overflow собрал сотни тысяч просмотров и десятки противоречивых ответов. Почему? Потому что JavaScript — это язык, где typeof NaN === 'number', а пустая строка считается нулём.

В этой статье мы не просто дадим готовую функцию isNumeric(). Мы разберём 5 самых частых и опасных ошибок, которые допускают даже опытные программисты, и покажем, как их избежать. Вы узнаете, почему isNaN — это ловушка, как работает приведение типов и какой метод валидации чисел в JS реально использовать в продакшене.

Почему isNaN() — зло (и когда оно работает)

Давайте начнём с очевидного. Многие новички пишут так:

function isNumeric(n) {
  return !isNaN(n);
}

На первый взгляд логично: если не NaN, значит число. Но давайте протестируем:

console.log(isNumeric('123'));    // true — ок
console.log(isNumeric('12.3'));   // true — ок
console.log(isNumeric(''));       // true — УПС! Пустая строка — число?
console.log(isNumeric(' '));      // true — пробел тоже число?
console.log(isNumeric(null));     // true — null — это число?
console.log(isNumeric(true));     // true — true — это 1

Ошибка №1: isNaN() сначала преобразует аргумент в число, а потом проверяет. Пустая строка '' преобразуется в 0, null — в 0, true — в 1. В результате функция считает числами то, что числами не является.

Есть строгий вариант — Number.isNaN() (ES6), но он проверяет только, является ли значение именно NaN, без преобразования. Для валидации чисел он тоже не годится.

parseFloat: герой или злодей?

Второй популярный подход — использовать parseFloat():

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

Этот вариант уже лучше, но у него другая проблема:

console.log(isNumeric('123abc')); // true — parseFloat откусит '123', а 'abc' проигнорирует
console.log(isNumeric('12.3.4')); // true — вторую точку parseFloat просто не заметит
console.log(isNumeric('Infinity')); // true — бесконечность — это число?

Ошибка №2: parseFloat() парсит строку до первого недопустимого символа. Строка '123abc' для него — это 123. Но пользователь мог случайно нажать лишнюю клавишу, и мы обязаны это отловить.

Ошибка №3: isFinite() отсекает Infinity и -Infinity, но пропускает NaN и undefined. Проблема в том, что isFinite(null) возвращает true, потому что null преобразуется в 0.

Ошибка №4: Регулярные выражения — панацея или новый ад?

Когда разработчики понимают, что встроенные функции ненадёжны, они часто бросаются в другую крайность — регулярные выражения. И тут начинается настоящий хаос.

Вот типичная, но опасная регулярка:

function isNumeric(n) {
  return /^\d+$/.test(n);
}

Проблемы:

  • Не пропускает десятичные точки ('12.3' — false)
  • Не пропускает отрицательные числа ('-5' — false)
  • Не пропускает научную нотацию ('1e5' — false)

Вот «улучшенная» версия, которую часто копируют с форумов:

function isNumeric(n) {
  return /^-?\d+(\.\d+)?$/.test(n);
}

Выглядит солидно, но пропускает пустую строку после знака минус ('-' — true). А ещё не учитывает пробелы и не работает с экспоненциальной записью.

Вывод: регулярные выражения — мощный инструмент, но для валидации чисел их нужно писать с учётом всех форматов, которые JavaScript считает числами. А это 0x1A (шестнадцатеричные), 0b101 (двоичные), 1e-3 (экспонента) и даже 0.1e+2.

Ошибка №5: Забываем про пробелы и пустые строки

Самая коварная ошибка — не обрезать строку перед проверкой. Пользователи часто случайно ставят пробел в начале или конце. Даже самая умная функция isNumeric споткнётся о ' 123 ', если не применить trim().

function isNumeric(n) {
  if (typeof n !== 'string') return false;
  return !isNaN(n) && !isNaN(parseFloat(n));
}

Этот код всё ещё пропустит ' 123 ' как число? Нет, parseFloat обрезает пробелы автоматически. Но Number(' 123 ') тоже вернёт 123. Проблема в другом: если вы не хотите принимать строки с пробелами, нужно явно это проверять.

Идеальная функция IsNumeric: пошаговая сборка

Теперь, когда мы разобрали все подводные камни, давайте напишем правильную функцию валидации десятичных чисел в JavaScript. Она должна:

  • Принимать только строки (или числа, если нужно)
  • Отсекать пустые строки и строки из пробелов
  • Проверять, что строка является конкретным числовым значением (не Infinity, не NaN)
  • Поддерживать десятичные дроби, отрицательные числа и научную нотацию
  • Не пропускать «мусор» типа '123abc'
function isNumeric(value) {
  // Если значение уже число — проверяем, не NaN и не Infinity
  if (typeof value === 'number') {
    return isFinite(value);
  }
  
  // Если не строка — сразу false
  if (typeof value !== 'string') {
    return false;
  }
  
  // Убираем пробелы                

Блоги

Рекомендаций по книгам