Как правильно проверить, является ли строка числом в JavaScript? Функция IsNumeric()
console.log(isNaN('123')); // false — вроде число
console.log(isNaN('12.3')); // false — тоже число
console.log(isNaN('')); // false — пустая строка считается числом?!
console.log(isNaN(' ')); // false — пробелы тоже число?
console.log(isNaN('abc')); // true — это понятно
console.log(isNaN(true)); // false — true=1, это число?!
console.log(isNaN(null)); // false — null=0, тоже число?!
Мне нужно, чтобы функция IsNumeric() возвращала true только для нормальных десятичных чисел (целых и дробных с точкой), а для пустых строк, пробелов, булевых значений, null, undefined и прочего мусора — false.
Ещё хочется, чтобы поддерживались отрицательные числа и числа с плавающей точкой (например, "-123.45"). А вот научная нотация (1e5) и шестнадцатеричные числа (0xFF) мне не нужны.
Подскажите, как написать такую функцию корректно и надёжно? Желательно с учётом разных браузеров и edge-случаев. Спасибо!
<<Проблема встроенных методов и правильное решение
Вы совершенно правы: isNaN() и Number() имеют неочевидное поведение. isNaN() сначала преобразует аргумент в число, поэтому '' становится 0, true — 1, null — 0. Это неприемлемо для валидации пользовательского ввода.
Лучший способ — использовать комбинацию typeof, parseFloat() и isFinite(). Вот оптимальная реализация:
function isNumeric(value) {
if (typeof value !== 'string' && typeof value !== 'number') {
return false;
}
return !isNaN(value) && !isNaN(parseFloat(value)) && isFinite(value);
}
Как это работает
- typeof value !== 'string' && typeof value !== 'number' — отсекаем булевы значения, null, undefined, объекты. Нам нужны только строки и числа.
- !isNaN(value) — проверяем, что само значение не является NaN (например,
NaNилиundefined). - !isNaN(parseFloat(value)) —
parseFloat()пытается извлечь число из строки. Для''и' 'он вернётNaN(что нам и нужно). Для'123abc'вернёт123— это может быть нежелательно, поэтому идём дальше. - isFinite(value) — отсекает
Infinity,-InfinityиNaN. Также проверяет, что число не слишком большое/маленькое.
Дополнительное улучшение: строгая проверка
Если нужно, чтобы строка содержала только число (без лишних символов вроде '123abc'), добавьте проверку с Number():
function isNumericStrict(value) {
if (typeof value !== 'string' && typeof value !== 'number') {
return false;
}
const num = Number(value);
return !isNaN(num) && isFinite(num) && String(num) === value.trim();
}
Здесь String(num) === value.trim() гарантирует, что после преобразования в число и обратно в строку мы получим то же самое. Например:
'123'→123→'123'— совпадает, true'12.30'→12.3→'12.3'— не совпадает, false (если это важно)' 123 '→123→'123'— не совпадает из-за пробелов
Тесты для обоих вариантов
// Первая функция (либеральная)
console.log(isNumeric('123')); // true
console.log(isNumeric('-123.45')); // true
console.log(isNumeric('0.5')); // true
console.log(isNumeric('')); // false
console.log(isNumeric(' ')); // false
console.log(isNumeric('abc')); // false
console.log(isNumeric(true)); // false
console.log(isNumeric(null)); // false
console.log(isNumeric(Infinity)); // false
// Вторая функция (строгая)
console.log(isNumericStrict('123')); // true
console.log(isNumericStrict(' 123 ')); // false (пробелы)
console.log(isNumericStrict('12.30')); // false (лишний ноль)
console.log(isNumericStrict('123abc'));// false
Рекомендация
Для большинства задач (валидация форм, ввод цен) достаточно первой версии. Вторая нужна, если критично точное совпадение с числовым форматом. Обе функции работают во всех современных браузерах и Node.js.
<<Альтернативный подход с регулярным выражением
Если вы уверены, что входные данные — всегда строка, можно использовать регулярное выражение. Оно даёт полный контроль над форматом:
function isNumericRegex(value) {
if (typeof value !== 'string') return false;
return /^-?\d+(\.\d+)?$/.test(value.trim());
}
Это выражение проверяет:
^-?— необязательный минус в начале\d+— одна или более цифр(\.\d+)?— необязательная дробная часть с точкой$— конец строки
Примеры
console.log(isNumericRegex('123')); // true
console.log(isNumericRegex('-123.45')); // true
console.log(isNumericRegex('0.5')); // true
console.log(isNumericRegex('.')); // false
console.log(isNumericRegex('12.')); // false
console.log(isNumericRegex('')); // false
console.log(isNumericRegex(' 123 ')); // false (пробелы)
Плюсы: максимально строгий контроль, легко модифицировать (добавить запятую, пробелы, научную нотацию).
Минусы: не работает с числами (только строки), нужно помнить про экранирование.
Выбирайте подход под свою задачу: для надёжности и гибкости — первый способ с isFinite, для жёсткой валидации формата — регулярное выражение.